我最近开始在项目中使用MapStruct映射工具。过去,为了映射 DTO -> 实体,反之亦然,我使用自定义映射器,例如:
public static CustomerDto toDto(Customer customer) {
return isNull(customer)
? null
: CustomerDto.builder()
.id(customer.getId())
.name(customer.getName())
.surname(customer.getSurname())
.phoneNumber(customer.getPhoneNumber())
.email(customer.getEmail())
.customerStatus(customer.getCustomerStatus())
.username(customer.getUsername())
.NIP(customer.getNIP())
.build();
}
万一当我尝试获取一个可选对象时,我能够通过以下方式将我的实体映射到 dto:
public Optional<CustomerDto> findOneById(final long id) {
return customerRepository.findById(id).map(CustomerMapper::toDto);
}
目前,正如我之前提到的,我正在使用mapStruct,问题是我的映射器不是类,而是像这样的接口:
@Mapper
public interface CommentMapper {
@Mappings({
@Mapping(target = "content", source = "entity.content"),
@Mapping(target = "user", source = "entity.user")
})
CommentDto commentToCommentDto(Comment entity);
@Mappings({
@Mapping(target = "content", source = "dto.content"),
@Mapping(target = "user", source = "dto.user")
})
Comment commentDtoToComment(CommentDto dto);
}
我想知道是否可以在流中以某种方式使用此接口方法来映射我的值而不包装值,例如:
public Optional<CommentDto> findCommentById(final long id) {
Optional<Comment> commentById = commentRepository.findById(id);
return Optional.ofNullable(commentMapper.commentToCommentDto(commentById.get()));
}
感谢您的帮助。
访问映射器,如下所示:
private static final YourMapper MAPPER = Mappers.getMapper(YourMapper.class);
final Optional<YourEntity> optEntity = entityRepo.findById(id);
return optEntity.map(MAPPER::toDto).orElse(null);
基本上我们对枚举做了类似的事情
@Mapping(target = "type", expression = "java(EnumerationType.valueOf(entity.getType()))")
您可以在@Mapping注释中定义java表达式
@Mapping(target = "comment", expression = "java(commentMapper.commentToCommentDto(commentRepository.findById(entity.commentId).orElse(null)))"
否则你应该能够使用
class CommentMapper { ... }
您可以自动参考
@Mapper(uses = {CommentMapper.class})
您的实现将检测 commentEntity 和 Dto 并自动使用 CommentMapper。
MapStruct 映射器的工作方式如下:Shit in Shit out,所以请记住您的实体需要 commentEntity,以便 dto 可以拥有 commentDto。
编辑
第二个解决方案可以使用:
@BeforeMapping
default void beforeMappingToDTO(Entity source, @MappingTarget Dto target) {
target.setComment(commentMapper.commentToCommentDto(commentRepository.findById(entity.commentId).orElse(null)));
}
@Spektakulatius 的回答解决了问题。
为了实现目标,我采取了几个步骤: 首先,我创建了一个映射器对象以在 java 流中使用它:
private CommentMapper commentMapper = Mappers.getMapper(CommentMapper.class);
在最后一步中,我使用了我的对象 commentMapper,例如:
public Optional<CommentDto> findCommentById(final long id) {
return commentRepository.findById(id).map(CommentMapper.MAPPER::commentToCommentDto);
}
毕竟这完全让我有可能在流中使用我的自定义 MapStruct 映射器。
另一种方法是在映射器中扩展功能接口,如下所示:
@Mapper
public interface MyMapper extends Function<MySource, MyTarget> {
}
在这种情况下,mapstruct 将实现 Function 中的 apply 方法。当您这样做时,您可以简单地使用映射器的实例作为映射函数的参数。在春天,你甚至可以使用像这样的函数类型注入你的映射器:
class MyService {
private final Function<MySource, MyTarget> mapper;
private final MyRepo repository;
// required arg constructor here
public Optional<MyTarget> findById(final long id) {
return repository.findById(id).map(mapper);
}
}
我认为这是一种非常优雅的方式。我在其他情况下也使用这种方法。为了映射到现有源,您可以使用 BiFunction 功能接口。也许这种方法的表面缺点是您必须为您想要以功能方式使用的每个映射方法拥有单独的映射器接口。