重构泛型类后CascadeType.ALL失效,求排查问题原因
出现的异常
- 保存Bar实体时抛出:
Caused by: java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.my.company.entity.FooCell - 加载Bar实体时抛出:
Table 'db.foo_foo_cell' doesn't exist
代码中的错误及修复方案
1. @OneToMany关联的mappedBy属性大小写错误
重构前Foo类的cells字段关联配置为mappedBy = "foo"(对应FooCell实体中的小写foo关联字段),但重构后在Foo类重写的getCells()方法上,错误地写成了mappedBy = "Foo"(大写F)。这会导致Hibernate无法识别正确的双向关联关系,进而无法触发cascade = CascadeType.ALL的级联保存逻辑,FooCell实例因未被级联保存处于transient状态,最终抛出TransientObjectException。
修复:
将Foo类中getCells()方法的mappedBy值改为小写的"foo":
@Access(AccessType.PROPERTY) @OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true) @Override public List<FooCell> getCells() { return super.getCells(); }
2. 基类CustomFooter的cells字段未正确配置双向关联
基类CustomFooter中的cells字段仅标注了@OneToMany,没有指定mappedBy属性,也未保留原有的cascade和orphanRemoval配置。即使子类重写时补充了配置,Hibernate的映射解析逻辑仍会将其识别为单向关联,自动生成默认的中间表foo_foo_cell,导致加载时出现表不存在的错误。
修复:
在基类CustomFooter的cells字段上补充双向关联的核心配置,子类可按需重写调整:
@OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true) List<CellType> cells;
(注:若不同子类的mappedBy值不同,可在子类中通过@AssociationOverride覆盖该配置,或者基类不指定mappedBy,完全由子类重写时配置)
3. @AssociationOverride的关联表名与原表不一致
重构前多对多关联的表名是foo__type(两个下划线),但重构后@AssociationOverride中指定的表名是foo_type(一个下划线),这会导致Hibernate访问错误的关联表,后续可能引发查询或保存异常。
修复:
将@AssociationOverride中的表名改为原有的foo__type:
@AssociationOverride( name="footerOptions", joinTable=@JoinTable( name = "foo__type", joinColumns = {@JoinColumn(name = "foo_id", nullable = true)}, inverseJoinColumns = {@JoinColumn(name = "type_id", nullable = true)} ) )
4. 基类未实现Comparable接口(非当前异常直接原因,但需修正)
基类CustomFooter中的compareTo方法没有实现Comparable接口,而重构前Foo类实现了Comparable<Foo>,这会导致编译或运行时的类型匹配问题。
修复:
让基类实现Comparable泛型接口:
@MappedSuperclass @Getter @Setter public abstract class CustomFooter<FooterOptions, CellType, FooterType> extends BaseEntity implements Comparable<CustomFooter<FooterOptions, CellType, FooterType>> { // ... 原有代码 @Override public int compareTo(CustomFooter<FooterOptions, CellType, FooterType> o) { return Integer.compare(this.sortOrder, o.getSortOrder()); } }
内容的提问来源于stack exchange,提问作者Daniela Morais

