Spring Boot+Hibernate中嵌套Embeddable的ElementCollection保存报NPE
问题分析与解决建议
错误根源
Hibernate 在处理 @Embeddable 类型时,需要能够设置类的属性值,但你的 Specification 和 ValueOfSpec 类中所有字段都使用了 final 修饰符。由于 final 字段初始化后无法被重新赋值,Hibernate 无法为这些字段生成对应的 Setter 方法,导致 PropertyAccess.getSetter() 返回 null,最终抛出 NullPointerException。
解决方案
1. 修改嵌入类的 final 字段
将 Specification 和 ValueOfSpec 中的 final 字段改为非 final,并添加对应的 Setter 方法(Hibernate 可通过反射访问字段,但添加 Setter 更符合规范):
修改后的 Specification 类
@Embeddable public class Specification implements Serializable { private String fieldName; private String fieldType; private boolean required; private boolean readOnly; public Specification() { this.fieldName = null; this.fieldType = null; this.required = false; this.readOnly = false; } public Specification(String fieldName, String fieldType, boolean required, boolean readOnly) { this.fieldName = fieldName; this.fieldType = fieldType; this.readOnly = readOnly; this.required = required; } // 保留原有 getter 方法 public String getFieldName() { return fieldName; } public String getFieldType() { return fieldType; } public boolean isReadOnly() { return readOnly; } public boolean isRequired() { return required; } // 添加 Setter 方法 public void setFieldName(String fieldName) { this.fieldName = fieldName; } public void setFieldType(String fieldType) { this.fieldType = fieldType; } public void setRequired(boolean required) { this.required = required; } public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } // 保留原有 equals、hashCode 方法 @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Specification that)) return false; return isRequired() == that.isRequired() && isReadOnly() == that.isReadOnly() && Objects.equals(getFieldName(), that.getFieldName()) && Objects.equals(getFieldType(), that.getFieldType()); } @Override public int hashCode() { return Objects.hash(getFieldName(), getFieldType(), isRequired(), isReadOnly()); } }
修改后的 ValueOfSpec 类
@Embeddable public class ValueOfSpec implements Serializable { private Specification specification; private String value; public ValueOfSpec(Specification specification, String value) { this.specification = specification; this.value = value; } public ValueOfSpec() { this.value = null; this.specification = null; } // 保留原有 getter 方法 public Specification getSpecification() { return specification; } public String getValue() { return value; } // 添加 Setter 方法 public void setSpecification(Specification specification) { this.specification = specification; } public void setValue(String value) { this.value = value; } // 保留原有其他方法 }
2. 额外检查
同时注意 Product 类中的 auditInformation 字段也是 final 修饰,如果该字段同样是需要 Hibernate 处理的嵌入类或实体属性,建议一并修改为非 final 并添加 Setter 方法,避免后续出现同类问题。
补充说明
如果希望保持类的不可变性,可以尝试给嵌入类添加 @Immutable 注解,但该注解会让 Hibernate 对该类实例只做读取操作,不执行更新逻辑。如果你的业务场景需要修改这些嵌入类的属性,去掉 final 修饰符是最直接有效的解决方案。
内容的提问来源于stack exchange,提问作者eynoc
相关产品推荐
相关产品推荐

