简体   繁体   English

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

[英]Map a dto to an entity retrieved from database if Dto has Id using MapStruct

I'm using MapStruct to make dto <-> entity mapping. 我正在使用MapStruct进行dto <-> entity映射。 The same mappers are used to create and update entities from dtos. 相同的映射器用于从dto 创建 更新实体。 A verification of the dto's id is done to know whether a new entity must be created (id == null) or it should be retrieved from database (id != null) . 完成dto的id验证以了解是否必须创建一个新实体(id == null)还是应该从数据库中检索它(id!= null)。

I'm actually using MapperDecorator as a workaround. 我实际上正在使用MapperDecorator作为解决方法。 Example : 范例:

Mapper 映射器

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

    UserAccountDto map(User user);

    User map(UserAccountDto dto);

    User map(UserAccountDto dto, @MappingTarget User user);
}

Decorator 装饰

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);
    }

}

But this solution becomes heavy due to the fact that a decorator must be created for each mapper. 但是,由于必须为每个映射器创建一个装饰器,因此该解决方案变得繁重。

Is there any good solution to perform that ? 有什么好的解决方案吗?


I'm using : 我正在使用 :

  1. MapStruct : 1.1.0 MapStruct: 1.1.0

I solved my problem by following the advice of Gunnar in the comment . 我按照评论Gunnar的建议解决了我的问题。

I moved to MapStruct 1.2.0.Beta1 and created a UserMapperResolver like below 我移至MapStruct 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();
    }

}

Which I use then in my UserMapper : 然后我在UserMapper中使用它:

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

    BaseUserDto map(User user);

    User map(BaseUserDto baseUser);

}

The generated code is now : 现在生成的代码是:

@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() );
...
}

Works well ! 效果很好 !

MapStruct alone can't do that. 仅MapStruct不能做到这一点。 However, with some generics and a main abstract class you can make your life easier. 但是,使用一些泛型和主要的抽象类,您可以使生活更轻松。

You need one generic interface. 您需要一个通用接口。 It must not be annotated with @Mapper , because if it is MapStruct will try to generate an implementation and it will fail. 它一定不能用@Mapper注释,因为如果它是MapStruct,它将尝试生成一个实现,并且将失败。 It cannot generate generic mappers. 它无法生成通用映射器。

public interface GenericMapper<E, DTO> {

    DTO map(E entity);

    E map(DTO dto);

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

Then you need one abstract class where you'll have your logic. 然后,您需要一个abstract类,在其中您将拥有自己的逻辑。

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();
}

And then each of your mappers will only need to extend this abstract class. 然后,每个映射器将只需要扩展此abstract类。

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

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

MapStruct will then generate an implementation for your mapper and you will only have to extend the AbstractGenericMapper for the future. 然后,MapStruct将为您的映射器生成一个实现,您只需要为将来扩展AbstractGenericMapper Of course you will need to adapt the generic parameters so you can at least get the id from the via some interface maybe. 当然,您将需要调整通用参数,以便至少可以通过某个接口从via获取ID。 If you have different type of ids then you will have to add that generic parameter to the AbstractGenericMapper as well. 如果您具有不同类型的ID,则还必须将该通用参数添加到AbstractGenericMapper中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM