如果 Dto 使用 MapStruct 具有 Id,则将 dto 映射到从数据库检索的实体
Map a dto to an entity retrieved from database if Dto has Id using MapStruct
我正在使用 MapStruct 进行 dto <-> entity
映射。相同的映射器用于从 dtos 创建 和 更新 实体。对 dto 的 id 进行验证以了解是否必须创建新实体(id == null)或者应该从数据库中检索它(id != null)。
我实际上使用 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.0
MapStruct 无法单独做到这一点。但是,使用一些泛型和主要摘要 class 您可以让您的生活更轻松。
您需要一个通用接口。它不能用 @Mapper
注释,因为如果它是 MapStruct 将尝试生成一个实现并且它将失败。它无法生成通用映射器。
public interface GenericMapper<E, DTO> {
DTO map(E entity);
E map(DTO dto);
E map(DTO dto, @MappingTarget E entity);
}
那么你需要一个 abstract
class 来表达你的逻辑。
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
class。
@Mapper
public abstract class UserAccountMapper extends AbstractGenericMapper<User, UserDto> {
protected User newInstance() {
return new User();
}
}
MapStruct 将为您的映射器生成一个实现,您将来只需扩展 AbstractGenericMapper
。当然,您需要调整通用参数,这样您至少可以通过某个接口从中获取 id。如果您有不同类型的 ID,那么您还必须将该通用参数添加到 AbstractGenericMapper
。
我按照 Gunnar in the 的建议解决了我的问题。
我搬到 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();
}
}
然后我在我的 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 <-> entity
映射。相同的映射器用于从 dtos 创建 和 更新 实体。对 dto 的 id 进行验证以了解是否必须创建新实体(id == null)或者应该从数据库中检索它(id != null)。
我实际上使用 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.0
MapStruct 无法单独做到这一点。但是,使用一些泛型和主要摘要 class 您可以让您的生活更轻松。
您需要一个通用接口。它不能用 @Mapper
注释,因为如果它是 MapStruct 将尝试生成一个实现并且它将失败。它无法生成通用映射器。
public interface GenericMapper<E, DTO> {
DTO map(E entity);
E map(DTO dto);
E map(DTO dto, @MappingTarget E entity);
}
那么你需要一个 abstract
class 来表达你的逻辑。
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
class。
@Mapper
public abstract class UserAccountMapper extends AbstractGenericMapper<User, UserDto> {
protected User newInstance() {
return new User();
}
}
MapStruct 将为您的映射器生成一个实现,您将来只需扩展 AbstractGenericMapper
。当然,您需要调整通用参数,这样您至少可以通过某个接口从中获取 id。如果您有不同类型的 ID,那么您还必须将该通用参数添加到 AbstractGenericMapper
。
我按照 Gunnar in the
我搬到 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();
}
}
然后我在我的 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() );
...
}
效果不错!