Skip to content

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

参考文档

MIT版权,未经许可禁止任何形式的转载