7

我正在使用MapStruct进行dto <-> entity映射。相同的映射器用于从 dto创建 更新实体。对 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.1.0
4

2 回答 2

18

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

效果很好 !

于 2017-02-22T10:39:40.177 回答
2

仅 MapStruct 无法做到这一点。然而,使用一些泛型和一个主要的抽象类,你可以让你的生活更轻松。

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

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,那么您还必须将该通用参数添加到 id 中AbstractGenericMapper

于 2017-02-21T18:22:05.330 回答