web-dev-qa-db-ja.com

DtoがMapStructを使用してIDを持っている場合、データベースから取得したエンティティにdtoをマップします

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

}

ただし、マッパーごとにデコレータを作成する必要があるため、このソリューションは重くなります。

それを実行するための良い解決策はありますか?


私が使用している:

  1. MapStruct:1.1.0
6
Radouane ROUFID

commentGunnar のアドバイスに従って問題を解決しました。

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

うまくいきます!

7
Radouane ROUFID

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に追加する必要があります。

2
Filip