【问题标题】:Hibernate OneToMany with Mismatched Composite Primary Keys使用不匹配的复合主键休眠 OneToMany
【发布时间】:2017-08-05 03:03:30
【问题描述】:

我的数据库中有两个表,它们通过复合主键/外键映射在一起,而且我在让 Hibernate 与它们一起工作时遇到了很多麻烦。我的数据库如下所示:

TABLE1 有一个由外键组成的复合主键,映射到 TABLE_A 和 TABLE_B。 TABLE2 还具有外键的复合主键,映射到 TABLE_A、TABLE_B 和 TABLE_D。在数据库中,TABLE2 仅使用前两个外键映射回 TABLE1。那里没有问题。它正在将其转换为 Hibernate,这正在杀死我。

因为 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" 失败。

我知道我可以很容易地解决这个问题,只需修改数据库并将单个 ID 主键添加到 TABLE1,并将单个 FK_TABLE1 主键添加到 TABLE2。然后我可以使用我的标准方法@OneToMany(mappedBy="table1"...)。但我真的希望避免这种情况,如果没有其他原因,我显然不需要在数据库级别这样做。我希望有一种方法可以告诉 Hibernate Table1 是所有者,并且对 Table2 的所有更改都依赖于它。

【问题讨论】:

    标签: java hibernate oracle11g


    【解决方案1】:

    天哪,这是一场噩梦。我终于弄清楚了,回想起来,这是我真的应该早点想到的事情。这对我有用,以防其他人将来遇到类似问题。

    问题在于 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() {
            ...
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-07-11
      • 1970-01-01
      • 2014-03-27
      • 1970-01-01
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多