MapStructを使用してdto <-> entity
マッピングを作成しています。同じマッパーを使用してcreateandupdatedtosからのエンティティ。 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);
}
}
ただし、マッパーごとにデコレータを作成する必要があるため、このソリューションは重くなります。
それを実行するための良い解決策はありますか?
私が使用している:
comment の Gunnar のアドバイスに従って問題を解決しました。
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だけではそれができません。ただし、いくつかのジェネリックスとメインの抽象クラスを使用すると、生活を楽にすることができます。
1つの汎用インターフェースが必要です。 MapStructの場合、実装を生成しようとして失敗するため、@Mapper
で注釈を付けないでください。一般的なマッパーを生成することはできません。
public interface GenericMapper<E, DTO> {
DTO map(E entity);
E map(DTO dto);
E map(DTO dto, @MappingTarget E entity);
}
次に、ロジックを配置する1つの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
に追加する必要があります。