@Builder导致mapstruct无法映射父类属性的问题
问题描述:remark为实体类的公共参数,是所有实体类的父类属性,VO类中包含remark属性,但是在编译过程中没有生成VO→Entity的remark硬编码
bean代码示例
java@Data public class CargoPactReqVO { // ID private String id; // 合同号 private String pactNo; // 备注 private String remark; // 其他字段... } @Data @Builder @TableName("CARGO_PACT") @NoArgsConstructor @AllArgsConstructor public class CargoPact extends DposDomain { private static final long serialVersionUID = 1L; // ID @TableField(value = "ID") private String id; // 合同号 @TableField(value = "PACT_NO", updateStrategy = FieldStrategy.NOT_NULL) private String pactNo; } @Data public class DposDomain implements Serializable { private static final long serialVersionUID = 1L; // 备注 private String remark; // 其他公共字段... }问题原因:@Builder构建Entity时默认是不带父属性的
解决方案:如果需要将属性映射到父类中,要么去掉@Builder注解,要么在mapstruct映射时让builder不生效,个人选择后者
java@Mapper(builder = @org.mapstruct.Builder(disableBuilder = true))代码示例
java// 解决问题之前mapstruct实例编译后的代码 @Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2025-07-23T16:11:27+0800", comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_381 (Oracle Corporation)" ) public class ConvertUtilImpl implements ConvertUtil { @Override public CargoPact cargoPactReqVO2CargoPact(CargoPactReqVO cargoPactReqVO) { if ( cargoPactReqVO == null ) { return null; } CargoPact cargoPact = new CargoPact(); // 缺少公共字段remark的映射关系 cargoPact.setId( cargoPactReqVO.getId() ); cargoPact.setPactNo( cargoPactReqVO.getPactNo() ); // 其他字段... return cargoPact; } } // 解决问题之后mapstruct实例编译后的代码 @Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2025-07-23T16:11:27+0800", comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_381 (Oracle Corporation)" ) public class ConvertUtilImpl implements ConvertUtil { @Override public CargoPact cargoPactReqVO2CargoPact(CargoPactReqVO cargoPactReqVO) { if ( cargoPactReqVO == null ) { return null; } CargoPact cargoPact = new CargoPact(); // 多了公共字段remark的映射关系 cargoPact.setRemark( cargoPactReqVO.getRemark() ); cargoPact.setId( cargoPactReqVO.getId() ); cargoPact.setPactNo( cargoPactReqVO.getPactNo() ); // 其他字段... return cargoPact; } }
参考文档
@Builder导致属性默认值丢失
问题描述:添加了@Builder注解的实体类的属性如果指定默认值,在打包构建后默认值丢失
代码示例
java@Data @Builder @TableName("CARGO_PACT") @NoArgsConstructor @AllArgsConstructor public class CargoPact extends DposDomain { private static final long serialVersionUID = 1L; // ID @TableField(value = "ID") private String id = "123"; // 合同号 @TableField(value = "PACT_NO", updateStrategy = FieldStrategy.NOT_NULL) private String pactNo; } // 验证是否会造成默认值丢失 class Test { public static void main(String[] args) { CargoPact cargoPact = CargoPact.builder().build(); System.out.println(cargoPact.getId()); // null,默认值丢失 } }解决方案:在配置默认值的属性上方增加 @Builder.Default 注解即可
java@Data @Builder @TableName("CARGO_PACT") @NoArgsConstructor @AllArgsConstructor public class CargoPact extends DposDomain { private static final long serialVersionUID = 1L; // ID @TableField(value = "ID") @Builder.Default private String id = "123"; // 合同号 @TableField(value = "PACT_NO", updateStrategy = FieldStrategy.NOT_NULL) private String pactNo; }替代方案:因为博主在使用@Builder注解 + Mapstruct 进行类型转换时遇到了问题,结合参考文档中提到的使用@Accessors注解避免@Builder带来的问题,博主认为@Accessors + Hutool在进行类型转换的时候确实比上一个方案更加地方便和容错率高
java@Data @Accessors(chain = true) public class CargoPact extends DposDomain { private static final long serialVersionUID = 1L; // ID @TableField(value = "ID") private String id; // 合同号 @TableField(value = "PACT_NO", updateStrategy = FieldStrategy.NOT_NULL) private String pactNo; } class Test { public static void main(String[] args) { // CargoPact cargoPact = CargoPact.builder().id("123").pactNo("456").build(); // System.out.println(cargoPact); CargoPact cargoPact = new CargoPact().setId("123").setPactNo("456"); System.out.println(cargoPact); } }
参考文档