当前位置: 首页 > 知识库问答 >
问题:

如果dto具有使用MapStruct的Id,则将dto映射到从数据库检索的实体

丁书
2023-03-14

我正在使用MapStruct制作dto

我实际上是在使用MapperDecorator作为解决方案。例子:

@Mapper
@DecoratedWith(UserAccountDecorator.class)
public interface UserAccountMapper {

    UserAccountDto map(User user);

    User map(UserAccountDto dto);

    User map(UserAccountDto dto, @MappingTarget User user);
}
public abstract class UserAccountDecorator implements UserAccountMapper {

    @Autowired
    @Qualifier("delegate")
    private UserAccountMapper delegate;

    @Autowired
    private UserRepository userRepository;

    @Override
    public User map(UserAccountDto dto) {
        if (dto == null) {
            return null;
        }

        User user = new User();
        if (dto.getId() != null) {
            user = userRepository.findOne(dto.getId());
        }

        return delegate.map(dto, user);
    }

}

但是,由于必须为每个映射器创建一个装饰器,这个解决方案变得非常重要。

有什么好的解决方案吗?

我正在使用:

  1. 地图结构:1.1.0

共有2个答案

景哲
2023-03-14

只有MapStruct无法做到这一点。然而,通过一些泛型和一个主要的抽象类,你可以让你的生活更轻松。

您需要一个通用接口。它不能用@Mapper注释,因为如果它是MapSTRt将尝试生成一个实现,它将失败。它不能生成通用映射器。

public interface GenericMapper<E, DTO> {

    DTO map(E entity);

    E map(DTO dto);

    E map(DTO dto, @MappingTarget E entity);
}

然后你需要一个抽象类,在这个类中你将拥有你的逻辑

public abstract class AbstractGenericMapper<E, DTO> implements GenericMapper<E, DTO> {

    @Autowired
    private Repository<E> repository;

    @Override
    public final E map (DTO dto) {
        if (dto == null) {
            return null;
        }

        // You can also use a Java 8 Supplier and pass it down the constructor
        E entity = newInstance();
        if (dto.getId() != null) {
            user = repository.findOne(dto.getId());
        }

        return map(dto, entity);
    }

    protected abstract E newInstance();
}

然后,您的每个映射器将只需要扩展这个抽象类。

@Mapper
public abstract class UserAccountMapper extends AbstractGenericMapper<User, UserDto> {

    protected User newInstance() {
        return new User();
    }
}

然后MapStruct将为您的mapper生成一个实现,您只需为将来扩展AbstractGenericMapper。当然,您需要调整通用参数,以便至少可以通过一些接口从中获取id。如果您有不同类型的ID,那么您还必须将该泛型参数添加到AbstractGenericMapper中。

淳于枫
2023-03-14

我按照冈纳在评论中的建议解决了问题。

我移动到MapSTRt 1.2.0. Beta1,并创建了一个UserMapperResolver,如下所示

@Component
public class UserMapperResolver {

    @Autowired
    private UserRepository userRepository;

    @ObjectFactory
    public User resolve(BaseUserDto dto, @TargetType Class<User> type) {
        return dto != null && dto.getId() != null ? userRepository.findOne(dto.getId()) : new User();
    }

}

然后在我的UserMapper中使用:

@Mapper(uses = { UserMapperResolver.class })
public interface BaseUserMapper {

    BaseUserDto map(User user);

    User map(BaseUserDto baseUser);

}

生成的代码现在是:

@Override
    public User map(BaseUserDto baseUser) {
        if ( baseUser == null ) {
            return null;
        }

        User user = userMapperResolver.resolve( baseUser, User.class );

        user.setId( baseUser.getId() );
        user.setSocialMediaProvider( baseUser.getSocialMediaProvider() );
...
}

很好用!

 类似资料:
  • 问题内容: 我正在使用 MapStruct 进行映射。相同的映射器用于从dto 创建 和 更新 实体。完成dto的id验证,以了解是否必须创建一个新实体(id == null)还是应该从数据库中检索它(id!= null)。 我实际上正在使用MapperDecorator作为解决方法。范例: 映射器 装饰器 但是,由于必须为每个映射器创建一个装饰器,因此该解决方案变得繁重。 有什么好的解决方案吗?

  • 我有一种情况,在一个DTO中有另一个DTO,我必须映射到它对应的实体。 我正在使用mapstruct,我已经有antherEntityMapper已经存在。 如何更改EntityMapper接口,以便我可以将一个另一个EntityDTO映射到另一个Entity? 谢谢

  • 我不熟悉Mapstruct,在特定用例中遇到问题 因此,如果我的来源属性hotmail.com我的目标属性应该收到“个人”,如果我的来源facebook.com我的目标应该收到“公司”。 我想用表达法,但没法绕过它。我该怎么做?

  • 我正在尝试使用AutoMapper在LLBLGen实体和DTO之间创建映射。 我的DTO如下所示: ParentEntity包含一个与DTO列表同名的ChildCollection和一个Id(需要忽略其他LLBL字段)。因此,当ParentEntity映射到父d to时,它也应该将ChildCollection映射到一个子列表。 这就是我到目前为止得到的: 这会导致Id被映射,但List的计数为0

  • 我开始使用JOOQ和dvd租赁商店数据库aka sakila。基本上,我想找一个演员和他的角色(我创建的表)。到目前为止,我想到了这个: 我希望能够将演员及其角色提取到一个对象中:DTO。我找到了这篇文章https://arnaudroger.github.io/blog/2017/03/02/jooq-one-to-many-without-dto.html但我发现使用sfm的解决方案过于冗长,

  • 在我的服务中,我试图将实体映射到,但是由于方法返回(),我不能像通常用于那样进行如下转换。它为方法抛出“不能在'iterable'中解析方法'stream'”错误。 那么,如何将这个实体映射到?