当前位置: 首页 > 知识库问答 >
问题:

使用不匹配的复合主键HibernateOneToMany

虞华翰
2023-03-14

我的数据库中有两个表是通过复合主键/外键映射在一起的,我正花了一段时间让Hibernate来处理它们。我的数据库如下所示:

TABLE1有一个由外键组成的复合主键,映射到TABLE_A和Table_B。TABLE2还有一个由外键组成的复合主键,映射到TABLE_A、TABLE_B和Table_D。在数据库中,TABLE2仅使用前两个外键映射回TABLE1。那里没问题。把它转换成冬眠真让我受不了。

因为TABLE2需要一个包含三列的嵌入式id,所以我不能使用@OneTomany注释的mappedBy参数。我得到外键数与主键列不匹配的预期错误。因此,我使用了@JoinColumns。这对于保存新的实体是非常有效的。但是,当我试图从TABLE2中删除一些映射时,遇到了一个问题,Hibernate试图在删除之前更新TABLE2,将FK_TABLE_A设置为null,这显然是不允许的。我所能找到的最好结果是,在映射xml中使用inverse=“true”可以解决这个问题,确保Hibernate知道,尽管使用了@JoinColumn,TABLE1实体应该是关系的所有者。但是我没有使用XML,而且我无法通过注释来计算等价物是什么。

以下是我目前掌握的情况:

@Entity
@AssociationOverrides({
    @AssociationOverride(name = "pk.tableA",
            joinColumns = @JoinColumn(name = "FK_TABLE_A")),
    @AssociationOverride(name = "pk.tableB",
            joinColumns = @JoinColumn(name = "FK_TABLE_B")) })
@Table(name="TABLE1")
public class Table1 extends BaseObject implements Serializable
{
private static final long serialVersionUID = 1L;

private Table1Id pk = new Table1Id();

@EmbeddedId
public Table1Id getPk() {
    return pk;
}
public void setPk(Table1Id pk) {
    this.pk = pk;
}

private TableC tableC;
@ManyToOne
@JoinColumn(name = "FK_TABLE_C", referencedColumnName = "ID", nullable = true)
public TableC getTableC () {
    return this.tableC;
}
public void setTableC(TableC tableC) {
    this.tableC= tableC;
}

private List<Table2> table2s;
@OneToMany(cascade = {CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.EAGER)
@JoinColumns({
    @JoinColumn(name="FK_TABLE_A", referencedColumnName="FK_TABLE_A"),
    @JoinColumn(name="FK_TABLE_B", referencedColumnName="FK_TABLE_B")
})
public List<Table2> getTable2s() {
    return table2s;
}
public void setTable2s(List<Table2> table2s) {
    this.table2s= table2s;
}

@Override
public boolean equals(Object o) {
    ...
}

@Override
public int hashCode() {
    ...
}

@Override
public String toString() {
    ...
}
}

@Embeddable
public class Table1Id extends BaseObject implements Serializable 
{
    private static final long serialVersionUID = 1L;

    private TableA tableA;
    private TableB tableB;

    @ManyToOne
    public TableA getTableA() {
        return tableA;
    }
    public void setTableA(TableA tableA) {
        this.tableA = tableA;
    }

    @ManyToOne
    public TableB getTableB() {
        return tableB;
    }
    public void setTableB(TableB tableB) {
        this.tableB= tableB;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}


@Entity
@AssociationOverrides({
        @AssociationOverride(name = "pk.tableA",
                joinColumns = @JoinColumn(name = "FK_TABLE_A")),
        @AssociationOverride(name = "pk.tableB",
                joinColumns = @JoinColumn(name = "FK_TABLE_B")),
        @AssociationOverride(name = "pk.tableD",
                joinColumns = @JoinColumn(name = "FK_TABLE_D")) })
@Table(name="TABLE2")
public class Table2 extends BaseObject implements Serializable
{
    private static final long serialVersionUID = 1L;

    private Table2Id pk = new Table2Id ();

    @EmbeddedId
    public Table2Id getPk() {
        return pk;
    }
    public void setPk(Table2Id pk) {
        this.pk = pk;
    }

    private Double value;
    @Column(name = "VALUE", nullable = false, insertable = true, updatable = true, precision = 2)
    @Basic
    public Double getValue() {
        return this.value;
    }
    public void setValue(Double value) {
        this.goal = goal;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}

@Embeddable
public class Table2Id extends BaseObject implements Serializable 
{
    private static final long serialVersionUID = 1L;

    private TableA tableA;
    @ManyToOne
    public TableA getTableA() {
        return tableA;
    }
    public void setTableA(TableA tableA) {
        this.tableA= tableA;
    }

    private TableB tableB;
    @ManyToOne
    public TableB getTableB() {
        return tableB;
    }
    public void setTableB(TableB tableB) {
        this.tableB= tableB;
    }

    private TableD tableD;
    @ManyToOne
    public TableD getTableD() {
        return this.tableD;
    }
    public void setTableD(TableD tableD) {
        this.tableD= tableD;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}

通常对于这样的关系,我只使用@OneTomany注释的mappedBy值,所有操作都运行良好--更新、插入和删除按预期和所需执行。但是考虑到底层表的构造方式是很奇怪的,我不能这样做。仅映射到Table2Id中的单个记录(mappedby=“pk.tableA”或mappedby=“pk.tableB”)将导致完全不正确的数据。我要求这两个字段都有一个适当的匹配,但据我所知,我不能在mappedby中列出多个列。mappedBy=“pk.tableA,pk.tableB”失败。

我知道,只需修改数据库并向TABLE1添加一个ID主键,向Table2添加一个FK_TABLE1主键,就可以轻松地解决这个问题。然后我就可以使用@OneTomany(MappedBy=“Table1”...)的标准方法。但我真的希望避免这种情况,如果没有其他原因,我显然不需要在数据库级别上这样做的话。我希望有一种方法可以告诉Hibernate Table1是所有者,对Table2的所有更改都依赖于它。

共有1个答案

杜建章
2023-03-14

天哪,这真是场噩梦。我终于想通了,回想起来,我真的应该早点想到这件事。以下是我的办法,以防将来有人遇到类似的问题。

问题是Table2嵌入式Id直接映射到与Table1嵌入式Id相同的实体。这是我想要的数据库,但不是我想要的Hibernate。相反,TableA和TableB的两个字段应该由Table1本身表示,并且关联重写是为了匹配。它们需要包含insertable=false和updatable=false,以便Table2不能对table1进行任何更改。在我的情况下,我只想要一个单向的关系。然后Table1可以使用@OneTomany注释的mappedBy参数直接映射到自身。这允许Table1控制关系。因此,代码应该是:

@Entity
@AssociationOverrides({
        @AssociationOverride(name = "pk.tableA",
                joinColumns = @JoinColumn(name = "FK_TABLE_A", nullable=false)),
        @AssociationOverride(name = "pk.tableB",
                joinColumns = @JoinColumn(name = "FK_TABLE_B", nullable=false)) })
@Table(name="TABLE1")
public class Table1 extends BaseObject implements Serializable
{
    private static final long serialVersionUID = 1L;

    private Table1Id pk = new Table1Id ();

    @EmbeddedId
    public Table1Id getPk() {
        return pk;
    }
    public void setPk(Table1Id pk) {
        this.pk = pk;
    }

    private TableC tableC;
    @ManyToOne
    @JoinColumn(name = "FK_TABLE_C", referencedColumnName = "ID", nullable = true)
    public TableC getTableC() {
        return this.tableC;
    }
    public void setTableC(TableC tableC) {
        this.tableC = tableC;
    }

    private List<Table2> table2s;
    @OneToMany(mappedBy="pk.table1", cascade = {CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.EAGER)
    public List<Table2> getTable2s() {
        return table2s;
    }
    public void setTable2s(List<Table2> table2s) {
        this.table2s= table2s;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}

@Entity
@AssociationOverrides({
        @AssociationOverride(name = "pk.table1",
                joinColumns = {
                        @JoinColumn(name = "FK_TABLE_A", nullable=false, insertable=false, updatable=false),
                        @JoinColumn(name = "FK_TABLE_B", nullable=false, insertable=false, updatable=false)
                        }),
        @AssociationOverride(name = "pk.tableD",
                joinColumns = @JoinColumn(name = "FK_TABLE_D", nullable=false)) })
@Table(name="TABLE2")
public class Table2 extends BaseObject implements Serializable
{
    private static final long serialVersionUID = 1L;

    private Table2Id pk = new Table2Id();

    @EmbeddedId
    public Table2Id getPk() {
        return pk;
    }
    public void setPk(Table2Id pk) {
        this.pk = pk;
    }

    private Double value;
    @Column(name = "VALUE", nullable = false, insertable = true, updatable = true, precision = 2)
    @Basic
    public Double getValue() {
        return this.value;
    }
    public void setValue(Double value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}

@Embeddable
public class Table2Id extends BaseObject implements Serializable 
{
    private static final long serialVersionUID = 1L;

    private Table1 table1;
    @ManyToOne
    @JoinColumn(nullable=false)
    public Table1 getTable1() {
        return this.table1;
    }
    public void setTable1(Table1 table1) {
        this.table1 = table1;
    }

    private TableD tableD;
    @ManyToOne
    @JoinColumn(nullable=false)
    public TableD getTableD() {
        return this.tableD;
    }
    public void setTableD(TableD tableD) {
        this.tableD = tableD;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}
 类似资料:
  • 问题内容: 如何使用复合主键作为外键?看来我的尝试无效。 问题答案: 该行: 是错的。您不能那样使用,这只是父表中PK约束的名称。要将复合主键用作外键,您必须向子表中添加相同数量(组成PK)的相同数据类型的列,然后在定义中使用这些列的组合:

  • 如何将复合主键用作外键?看来我的尝试没有成功。

  • 问题内容: 我正在设计一个数据库,该数据库将用于存储来自许多不同来源的数据。我存储的实例由原始来源分配了唯一的ID。我存储的每个实例都应包含有关其来源的信息,以及与此来源相关联的ID。 作为示例,请考虑说明该问题的下表: 请注意,尽管每个来源的唯一,但有可能在不同来源中找到相同的来源。 我对关系数据库有一个不错的了解,但是与专家甚至是经验丰富的用户都相去甚远。我在此设计中面临的问题是应该用作主键。

  • 我有一些实体: 当我试图保存新的cbonus记录时,出现异常: org.postgresql.util.PSQLException: ERROR: null值在列"bank_id"的关系"cBonus"违反了非空约束详细信息:失败的行包含(773, gp3, null, null, f)。 和查询 DEBUG 24817-[nio-8080-exec-4]org . hibernate . SQL

  • 我是AWS的新手,我正在尝试通过Lambda更新DynamoDB中的一个item属性。我尝试了Node.JS和Python中的代码,两次尝试都返回: 模块初始化错误:调用UpdateItem操作时发生错误(ValidationException):提供的键元素与架构不匹配 为什么会发生这种情况?这是我的表格信息: 表名:高端假日拍卖 主分区键:Aliyah_ID(字符串) 主排序键:最高投标人(字

  • 问题内容: 我的JPA模型中有以下类(省略了getters,setters和无关字段): 我需要定义一个类,使得当从所述类生成DDL时,相应的表的主键被由密钥和。我尝试了以下方法: 但这会为表生成以下内容: 请注意,和都是可为空的,当我尝试将DDL加载到SQL Server时会导致以下错误 无法在表“ PRICE”中的可为空的列上定义PRIMARY KEY约束 我不明白为什么这些可以为空,因为在域