You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 02:17:55