如果DTO使用MAPSTRUCTER ID,则将DTO映射到从数据库中检索的实体



我正在使用 mapsstruct 进行dto <-> entity映射。相同的映射器用于创建更新来自DTOS的实体。完成了DTO ID的验证是为了知道是否必须创建一个新实体(ID == null),否则应从数据库中检索(ID!= null)。

我实际上使用mapperDecorator作为解决方法。示例:

mapper

@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. mapstruct: 1.1.0

我在评论中遵循Gunnar的建议解决了我的问题。

我搬到了mapsstruct 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() );
...
}

效果很好!

单独映射无法做到这一点。但是,有了一些仿制药和主要的抽象课,您可以使您的生活更轻松。

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

public interface GenericMapper<E, DTO> {
    DTO map(E entity);
    E map(DTO dto);
    E map(DTO dto, @MappingTarget E entity);
}

然后您需要一个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();
}

,然后您的每个映射器只需要扩展此abstract类。

@Mapper
public abstract class UserAccountMapper extends AbstractGenericMapper<User, UserDto> {
    protected User newInstance() {
        return new User();
    }
}

MAPSTRUCT将为您的映射器生成实现,您只需要在将来扩展AbstractGenericMapper即可。当然,您需要调整通用参数,以便至少可以从通过某些接口获得ID。如果您有不同类型的ID,则还必须将该通用参数添加到AbstractGenericMapper

最新更新