在项目里遇到了需要使用mapstruct将source对象的多个属性转为target对象的一个属性的场景。针对这个问题研究了一段时间,发现想要解决得好一些还是挺让人头疼的。
先说结论吧:MapStruct支持将多个对象转为一个对象,但是不支持将多个属性转为一个属性。对,mapstruct是不支持这么做的。
最终的解决方案也非常简单:在使用mapstruct完成对象的简单转换后,再做一次加工就行。不过我想将这个事情做得优雅一些,目的是尽量不影响业务代码。
项目的代码不好拿出来,举个例子来说明下,在下面的代码中定义了一个产品的Entity类及相应的Item类。目标是将Entity类的实例通过mapstruct转为Item类的实例。
先看下类的定义:
产品Entity类 ProductEntity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Data public class ProductEntity { /** * 名称 */ private String name; /** * 保质期 */ private Integer qualityGranteeMonths; /** * 生产日期 */ private LocalDate manufactureDate; } |
产品Item类 ProductItem :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Data public class ProductItem { /** * 名称 */ private String name; /** * 保质期 */ private Integer qualityGranteeMonths; /** * 生产日期 */ private LocalDate manufactureDate; /** * 状态 */ private ProductStatusEnum status; } |
ProductItem 比 ProductEntity 多了一个 status 属性,这个status属性可以由 生产日期 (manufactureDate)和保质期(qualityGranteeMonths)计算出来。计算逻辑可以看下 ProductStatusEnum 的定义及类中静态的analyze方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public enum ProductStatusEnum { /** * 有效 */ EFFECTIVE, /** * 过期 */ EXPIRED,; /** * 根据生产日期和保质期判断产品状态 * * @param manufactureDate 生产日期 * @param qualityGranteeMonths 保质期 * @return 产品状态 */ public static ProductStatusEnum analyzeStatus(LocalDate manufactureDate, Integer qualityGranteeMonths) { if (null == manufactureDate || null == qualityGranteeMonths) { return null; } ProductStatusEnum status = ProductStatusEnum.EFFECTIVE; if (manufactureDate.plusMonths(qualityGranteeMonths) .isBefore(LocalDate.now())) { status = ProductStatusEnum.EXPIRED; } return status; } } |
要实现基于 ProductEntity 的生产日期和保质期两个字段映射出Item的status的值,可以在完成 Entity和Item的转换后再做一次处理,类似下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ProductItem entity2ItemSimply(ProductEntity entity); default ProductItem entity2Item(ProductEntity entity) { if (null == entity) { return null; } ProductItem result = entity2ItemSimply(entity); result.setStatus(ProductStatusEnum.analyzeStatus(entity.getManufactureDate(), entity.getQualityGranteeMonths())); return result; } |
如上面的代码:在转换接口 ProductConverter 中,定义了一个default方法entity2Item,在这个方法中利用mapstruct生成的 entity2ItemSimply() 方法完成简单转换后又做了一次 生产日期、保质期和状态的映射。因为是在同一个转换接口中定义的,在使用时还是比较丝滑的。
不过有一个小问题,就是现在转换接口 ProductConverter 中存在两个将 Entity转为Item的方法,在处理相关Collection 转换时就会出现因为不知道该调用哪个方法而产生的报错,如下:
1 2 |
Ambiguous mapping meth ods found for mapping collection element to ProductItem: ProductItem entity2ItemSimply(ProductEntity entity), ProductItem entity2Item(ProductEntity entity). |
要解决这个问题也比较简单,使用 qualifyByName 进行标记即可, 最终代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Mapper public interface ProductConverter { ProductItem entity2ItemSimply(ProductEntity entity); @Named("entity2Item") default ProductItem entity2Item(ProductEntity entity) { if (null == entity) { return null; } ProductItem result = entity2ItemSimply(entity); result.setStatus(ProductStatusEnum.analyzeStatus(entity.getManufactureDate(), entity.getQualityGranteeMonths())); return result; } @IterableMapping(qualifiedByName = "entity2Item") List<ProductItem> entityList2ItemList(List<ProductEntity> entityList); } |
对了,不要忘了给 entity2Item() 方法加上 @Name 注解,不然会报相关方法找不到的错的。
就这样。源码在这里: zhy-explore / mapstruct-explore
End !!!
发表评论