2017-03-14 51 views
0

我的數據庫中有兩個表通過複合主鍵/外鍵映射到一起,我有一段時間讓Hibernate與它們一起工作。我的數據庫看起來是這樣的:休眠OneToMany與不匹配的複合主鍵

Simple ERD

TABLE1有外鍵的複合主鍵,映射TABLE_A和表-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」失敗。

我知道我可以通過修改數據庫並向TABLE1添加單個ID主鍵以及向TABLE2添加單個FK_TABLE1主鍵來輕鬆解決此問題。然後,我可以使用@OneToMany的標準方法(mappedBy =「table1」...)。但是我真的希望避免這種情況,如果沒有其他原因,我顯然不需要在數據庫級別這樣做。我希望有一種方法可以告訴Hibernate Table1是所有者,並且Table2的所有更改都依賴於它。

回答

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() { 
     ... 
    } 
}