Gott, das war ein Albtraum. Ich habe es endlich herausgefunden und im Nachhinein hätte ich wirklich früher daran denken sollen. Hier ist, was für mich funktioniert hat, falls jemand anderes in Zukunft ein ähnliches Problem hat.
Das Problem besteht darin, dass die eingebettete ID von Table2 direkt denselben Entitäten zugeordnet wurde wie die eingebettete ID von Table1. Das will ich mit der Datenbank, aber nicht mit Hibernate. Stattdessen sollten die beiden Felder für TableA und TableB durch Table1 selbst dargestellt werden, und die Assoziationsüberschreibungen müssen entsprechend geschrieben werden. Sie müssen insertable=false und updatetable=false enthalten, damit Table2 keine Änderungen an Table1 vornehmen kann. In meinem Fall möchte ich nur eine unidirektionale Beziehung. Table1 kann dann den mappedBy-Parameter der @OneToMany-Anmerkung verwenden, um sich selbst direkt zuzuordnen. Dadurch kann Table1 die Beziehung steuern. Der Code sollte also lauten:
@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() {
...
}
}