使用@DiscriminatorColumn映射类时更新对象遇重复标识符问题求助
问题:Hibernate单表继承+嵌入式ID更新时触发重复标识符异常
背景信息
我们的应用基于Spring和Hibernate 5.2开发,采用单表继承策略,实体类结构如下:
抽象父类 MyAbstractClass
@Entity @Table(name = "MYTABLE") @DiscriminatorColumn(name = "DISCR", discriminatorType = DiscriminatorType.STRING) public abstract class MyAbstractClass extends PersistentEntity { @EmbeddedId private MyClassPK myClassPK; @Column(name = "label") private String label; }
嵌入式ID类 MyClassPK
@Embeddable public class MyClassPK implements Serializable { @Column(name = "key1") @NotNull private String key1; @Column(name = "key2") @NotNull private String key2; }
子类 FirstChild
@Entity @DiscriminatorValue("DI1") @DiscriminatorOptions(force = true) public class FirstChild extends MyAbstractClass { }
子类 SecondChild
@Entity @DiscriminatorValue("DI2") @DiscriminatorOptions(force = true) public class SecondChild extends MyAbstractClass { }
问题场景
数据库中存在两条记录,它们的key1和key2完全相同,仅鉴别器列DISCR不同:
key1|key2|discr |label | ----+----+------+----------+ A | B | DI1 |label 1 | A | B | DI2 |label 2 |
当更新其中一个对象(比如FirstChild实例)时,Hibernate生成的SQL未包含鉴别器条件:
update MYTABLE set LABEL=? where KEY1=? and KEY2=?
这会导致更新同时命中两条记录,触发Duplicate identifier异常。我们期望的正确更新语句应该包含鉴别器过滤:
update MYTABLE set LABEL=? where KEY1=? and KEY2=? and DISCR='DI1'
已知Hibernate默认仅将修改列加入更新语句,请问如何处理才能避免该异常?
解决方案
方案1:将鉴别器列纳入嵌入式ID
修改MyClassPK,把DISCR字段加入嵌入式ID,让Hibernate自动在更新条件中包含该列:
@Embeddable public class MyClassPK implements Serializable { @Column(name = "key1") @NotNull private String key1; @Column(name = "key2") @NotNull private String key2; // 新增鉴别器字段,映射到DISCR列 @Column(name = "DISCR") private String discr; }
需要注意在实例化子类时,手动将对应鉴别器值(DI1/DI2)设置到ID的discr字段中,确保唯一性约束生效。
方案2:使用@Where注解强制过滤鉴别器
在每个子类上添加@Where注解,指定固定的鉴别器条件,Hibernate会在更新、查询时自动带上该条件:
@Entity @DiscriminatorValue("DI1") @DiscriminatorOptions(force = true) @Where(clause = "DISCR='DI1'") public class FirstChild extends MyAbstractClass { }
@Entity @DiscriminatorValue("DI2") @DiscriminatorOptions(force = true) @Where(clause = "DISCR='DI2'") public class SecondChild extends MyAbstractClass { }
该方案无需修改ID结构,配置简单,Hibernate会自动在生成更新语句时追加鉴别器过滤条件。
方案3:自定义SQL更新语句
通过@SQLUpdate注解为每个子类指定自定义更新SQL,明确包含鉴别器条件:
@Entity @DiscriminatorValue("DI1") @DiscriminatorOptions(force = true) @SQLUpdate(sql = "update MYTABLE set LABEL=? where KEY1=? and KEY2=? and DISCR='DI1'") public class FirstChild extends MyAbstractClass { }
@Entity @DiscriminatorValue("DI2") @DiscriminatorOptions(force = true) @SQLUpdate(sql = "update MYTABLE set LABEL=? where KEY1=? and KEY2=? and DISCR='DI2'") public class SecondChild extends MyAbstractClass { }
这种方式最直接,但需要手动维护SQL语句,当实体字段变更时需同步更新对应SQL。
内容的提问来源于stack exchange,提问作者QuickFix
相关产品推荐
相关产品推荐

