Hibernate OneToMany具有不匹配的复合主键

时间:2017-03-14 16:40:20

标签: java hibernate oracle11g

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

Simple ERD

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,这显然是不允许的。我能找到的最好的是使用inverse =" true"在映射xml中可能会解决问题,确保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 =&#34; pk.tableA&#34;或mappedBy =&#34; pk.tableB&#34;)将导致完全不正确的数据。我要求两个字段都有适当的匹配,但我可以告诉我不能在mappedBy中列出多个列。 mappedBy =&#34; pk.tableA,pk.tableB&#34;失败。

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

1 个答案:

答案 0 :(得分:0)

上帝,这是一场噩梦。我终于想通了,回想起来,这是我应该早点想到的。如果其他人在将来遇到类似的问题,这对我有用。

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