【问题标题】:JPA/Hibernate - mixed inheritance strategy using @SecondaryTable results in primary key violationJPA/Hibernate - 使用 @SecondaryTable 的混合继承策略导致主键冲突
【发布时间】:2020-11-20 08:44:03
【问题描述】:

我在尝试使用 JPA 实现混合继承策略时遇到问题。使用 Hibernate 5.2.18 (JPA 2.1)、5.3.5 (JPA 2.2) 和 5.4.22 (JPA 2.2) 时观察到相同的行为(两个插入 - 每个子类级别一个)。

我相信我的实现与这些线程中的建议是一致的,但也许我错过了一个微妙之处:

@Entity
@Table(name = "TABLE_A")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
abstract class A {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    public void setId(long id) {
        this.id = id;
    }

    public long getId() {
        return id;
    }
}

@Entity
@SecondaryTable(name = "TABLE_B")
@DiscriminatorValue("B")
class B extends A {

    @Column(table = "TABLE_B")
    private String property1;

    public String getProperty1() {
        return property1;
    }

    public void setProperty1(String property1) {
        this.property1 = property1;
    }
}

@Entity
@SecondaryTable(name = "TABLE_B")
@DiscriminatorValue("C")
class C extends B {

    @Column(table = "TABLE_B")
    private String property2;

    public String getProperty2() {
        return property2;
    }

    public void setProperty2(String property2) {
        this.property2 = property2;
    }
}

尝试持久化(插入)C 类型的对象会导致休眠尝试对TABLE_B 执行两次插入:

insert into TABLE_A (id, DTYPE) values (null, 'C')
Natively generated identity: 2
insert into TABLE_B (property1, id) values (?, ?)
binding parameter [1] as [VARCHAR] - [foo]
binding parameter [2] as [BIGINT] - [2]
insert into TABLE_B (property2, id) values (?, ?)
binding parameter [1] as [VARCHAR] - [bar]
binding parameter [2] as [BIGINT] - [2]
could not execute statement [n/a] 
org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PRIMARY KEY ON TABLE_B(ID) [2, 'foo', NULL]"; 

最后,如果我将 B 更改为 @MappedSuperclass 而不是 @Entity,我可以正确地保留 C 对象,但这会改变语义,我不能再将 B 对象保留为它们本身不再是实体。

谢谢,

编辑:

这是我的 DDL:

CREATE TABLE TABLE_A  (
    ID BIGINT IDENTITY NOT NULL PRIMARY KEY,
    DTYPE VARCHAR(16) NOT NULL
);

CREATE TABLE TABLE_B  (
    ID BIGINT IDENTITY NOT NULL PRIMARY KEY,
    PROPERTY1 VARCHAR(32),
    PROPERTY2 VARCHAR(32),
    CONSTRAINT FK_TABLE_B_PARENT FOREIGN KEY (ID) REFERENCES TABLE_A (ID)
);

我会附上一个 ER 图,但由于这是我的第一篇文章,我没有最低限度的声誉可以让我这样做。

【问题讨论】:

  • 我还应该补充一点,这个问题类似于this未回答的问题,但上下文不同
  • 能否请您展示您的架构相关部分的 ER 图。

标签: hibernate jpa inheritance polymorphism secondary-table


【解决方案1】:

我遇到了类似的问题,我可以使用 @MappedSuperclass 解决它。

防止多次插入的最简单方法是将所有属性向下移动到类 B 之后。您可以在 B 和 C 之间创建一个抽象类并使用 @MappedSuperclass 注释,如果您希望保持单独定义的属性,同时将 B 保持为托管类型。

这是完整的层次结构:

@Entity
@Table(name = "TABLE_A")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
class A {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    public void setId(long id) {
        this.id = id;
    }

    public long getId() {
        return id;
    }
}

/**
 * This will maintain B's interface since the method definitions will now be in an abstract subclass.
 */
interface BProperties {
    String getProperty1();
    void setProperty1(String property1);
}

@Entity
abstract class B extends A implements BProperties {

}

@MappedSuperclass
abstract class AbstractB extends B {

    @Column(table = "TABLE_B")
    private String property1;

    public String getProperty1() {
        return property1;
    }

    public void setProperty1(String property1) {
        this.property1 = property1;
    }
}

@Entity
@SecondaryTable(name = "TABLE_B")
@DiscriminatorValue("C")
class C extends AbstractB {

    @Column(table = "TABLE_B")
    private String property2;

    public String getProperty2() {
        return property2;
    }

    public void setProperty2(String property2) {
        this.property2 = property2;
    }
}

(如果您不需要 B 作为托管类型,那么您不需要额外的 AbstractB 类;您可以将属性保留在 B 中并用@MappedSuperclass 标记该类)

这里的问题是 B 类不能再包含属性。如果是这样,由于多次插入的问题,它的所有子代都将失败。如果您确实需要一个具有仅在 B 中定义的属性的具体类(或者现在具有此模式的 AbstractB),那么您可以扩展 AbstractB 并使用 @SecondaryTable 和 @Entity 注释声明该类。示例:

@Entity
@SecondaryTable(name = "TABLE_B")
@DiscriminatorValue("B")
class ConcreteB extends AbstractB {

}

【讨论】:

    猜你喜欢
    • 2011-12-02
    • 1970-01-01
    • 2011-04-24
    • 2012-04-17
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 2011-09-20
    相关资源
    最近更新 更多