Relationen mit zusammengesetzten Schlüsseln

Langsam bin ich mit meinem Latein am Ende. Ich versuche Relationen mit zusammengesetzten Schlüsseln zu realisieren und erhalte die verschiedensten Fehlermeldungen.

Ausgangssituation:

Tabelle TEIL, Schlüssel=FUE_GR_ID+VARIANTE_ID+TEIL_ID
FUE_GR_ID|VARIANTE_ID|TEIL_ID|ERSTELLER|ERDATUM

Tabelle REL_TEIL_WERT, Schlüssel=FUE_GR_ID+VARIANTE_ID+TEIL_ID+WERT
FUE_GR_ID|VARIANTE_ID|TEIL_ID|WERT

Versuch 1:

@Table(name="TEIL")
public class Teil implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @EmbeddedId
    protected TeilPK teilPK;
    
    @Basic(optional = false)
    private String ersteller;
    
    @Basic(optional = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date erdatum;
    
    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "teil")
    private List<RelTeilWert> relTeilWert;

    //..
}

@Embeddable
public class TeilPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;
  
    //..
}


@Entity
@Table(name = "REL_TEIL_WERT")
public class RelTeilWert implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected RelTeilKvsPK relTeilWertPK;
    
    @PrimaryKeyJoinColumns({
        @PrimaryKeyJoinColumn(name="FUE_GR_ID", referencedColumnName="FUE_GR_ID"),
        @PrimaryKeyJoinColumn(name="VARIANTE_ID", referencedColumnName="VARIANTE_ID"),
        @PrimaryKeyJoinColumn(name="TEIL_ID", referencedColumnName="TEIL_ID")
    })
    @ManyToMany
    private List<Teil> teil;
    
    //..
}

@Embeddable
public class RelTeilWertPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;

    @Basic(optional = false)
    @Column(name = "WERT")
    private String wert;

Fehlermeldung:
org.hibernate.HibernateException: Missing table: REL_TEIL_WERT_TEIL

Versuch 2:

@Table(name="TEIL")
public class Teil implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @EmbeddedId
    protected TeilPK teilPK;
    
    @Basic(optional = false)
    private String ersteller;
    
    @Basic(optional = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date erdatum;
    
    @PrimaryKeyJoinColumns({
        @PrimaryKeyJoinColumn(name="FUE_GR_ID", referencedColumnName="FUE_GR_ID"),
        @PrimaryKeyJoinColumn(name="VARIANTE_ID", referencedColumnName="VARIANTE_ID"),
        @PrimaryKeyJoinColumn(name="TEIL_ID", referencedColumnName="TEIL_ID")
    })
    @ManyToMany
    private List<RelTeilWert> relTeilWert;

    //..
}

@Embeddable
public class TeilPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;
  
    //..
}


@Entity
@Table(name = "REL_TEIL_WERT")
public class RelTeilWert implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected RelTeilKvsPK relTeilWertPK;
    
    
    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "relTeilWert")
    private List<Teil> teil;
    
    //..
}

@Embeddable
public class RelTeilWertPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;

    @Basic(optional = false)
    @Column(name = "WERT")
    private String wert;

Fehlermeldung:
Missing column: fueGr_FUE_GR_ID in TEIL

Versuch 3:

@Table(name="TEIL")
public class Teil implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @EmbeddedId
    protected TeilPK teilPK;
    
    @Basic(optional = false)
    private String ersteller;
    
    @Basic(optional = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date erdatum;
    
    @JoinTable(name="REL_TEIL_KVS", joinColumns={
        @JoinColumn(name="FUE_GR_ID", referencedColumnName="FUE_GR_ID"),
        @JoinColumn(name="VARIANTE_ID", referencedColumnName="VARIANTE_ID"),
        @JoinColumn(name="TEIL_ID", referencedColumnName="TEIL_ID")
    })
    @ManyToMany
    private List<RelTeilWert> relTeilWert;

    //..
}

@Embeddable
public class TeilPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;
  
    //..
}


@Entity
@Table(name = "REL_TEIL_WERT")
public class RelTeilWert implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected RelTeilKvsPK relTeilWertPK;
    
    
    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "relTeilWert")
    private List<Teil> teil;
    
    //..
}

@Embeddable
public class RelTeilWertPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;

    @Basic(optional = false)
    @Column(name = "WERT")
    private String wert;

Fehlermeldung:
org.hibernate.HibernateException: Missing column: relTeilWert_FUE_GR_ID in VT.REL_TEIL_WERT

Versuch 4:

@Table(name="TEIL")
public class Teil implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @EmbeddedId
    protected TeilPK teilPK;
    
    @Basic(optional = false)
    private String ersteller;
    
    @Basic(optional = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date erdatum;
    
    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "relTeilWert")
    private List<RelTeilWert> relTeilWert;

    //..
}

@Embeddable
public class TeilPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;
  
    //..
}


@Entity
@Table(name = "REL_TEIL_WERT")
public class RelTeilWert implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected RelTeilKvsPK relTeilWertPK;
    
    @JoinTable(name="REL_TEIL_KVS", joinColumns={
        @JoinColumn(name="FUE_GR_ID", referencedColumnName="FUE_GR_ID"),
        @JoinColumn(name="VARIANTE_ID", referencedColumnName="VARIANTE_ID"),
        @JoinColumn(name="TEIL_ID", referencedColumnName="TEIL_ID")
    })
    @ManyToMany
    private List<Teil> teil;
    
    //..
}

@Embeddable
public class RelTeilWertPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;

    @Basic(optional = false)
    @Column(name = "WERT")
    private String wert;

Fehlermeldung:
org.hibernate.AnnotationException: referencedColumnNames(FUE_GR_ID, VARIANTE_ID, TEIL_ID) of myPackage.Teil.teil referencing myPackage.RelTeilWert not mapped to a single property

Ich hab schon im HibernateManual geschaut und auch das allwissende Orakel “Google” gefragt und komme kein Stück weiter.

Erstmal zum Verständnis für mich: muss das wirklich eine @ManyToMany-Beziehung sein? Welcher Sachverhalt soll damit abgebildet werden?

eigentlich ist es es nur eine OneToMany-Beziehung, allerdings kann ein Teil auch mal keinen Wert haben, deswegen eine ManyToMany Im eigentlichen Projekt hab 3 Tabellen auf die sich die Werte aufteilen, je nach Art des Teils. Der Einfachheit wegen, wollte ich erstmal nur eine Relation versuchen abzubilden.

Ich arbeite mit Hibernate 3.2.5, wobei ich schon überlege eine aktuellere Version zu nehmen.

Auch bei einer OneToMany-Beziehung müssen nicht zwingend Objekte auf der Many-Seite existieren.

Leider funktioniert auch das nicht :frowning:

Mein Klassen sehen jetzt so aus:

@Table(name="TEIL")
public class Teil implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @EmbeddedId
    protected TeilPK teilPK;
    
    @Basic(optional = false)
    private String ersteller;
    
    @Basic(optional = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date erdatum;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "teil")
    private List<RelTeilWert> relTeilWert;

    //..
}

@Embeddable
public class TeilPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;
  
    //..
}


@Entity
@Table(name = "REL_TEIL_WERT")
public class RelTeilWert implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected RelTeilKvsPK relTeilWertPK;
    
    @PrimaryKeyJoinColumns({
        @PrimaryKeyJoinColumn(name="FUE_GR_ID", referencedColumnName="FUE_GR_ID"),
        @PrimaryKeyJoinColumn(name="VARIANTE_ID", referencedColumnName="VARIANTE_ID"),
        @PrimaryKeyJoinColumn(name="TEIL_ID", referencedColumnName="TEIL_ID")
    })
    @ManyToOne
    private Teil teil;
    
    //..
}

@Embeddable
public class RelTeilWertPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "FUE_GR_ID")
    private long fueGrId;

    @Basic(optional = false)
    @Column(name = "VARIANTE_ID")
    private long varianteId;

    @Basic(optional = false)
    @Column(name = "TEIL_ID")
    private long teilId;

    @Basic(optional = false)
    @Column(name = "WERT")
    private String wert;

Fehlermeldung:
org.hibernate.HibernateException: Missing column: teil_FUE_GR_ID in REL_TEIL_WERT

hängt das eventuel mit dem EmbeddedId’s zusammen? Die Klassen hab ich mir von Netbeans generieren lassen, nur die Relation von Teil zu RelTeilWert hab ich geschrieben.

*** Edit ***

so, jetzt klappt es mit einer @OneToMany<->@ManyToOne-Bezeihung, mit @JoinColumns({ @JoinColumn(name="…", referncedColumnName="…", insertable=false, updateable=false), …}) anstatt @PrimaryKeyJoinColumns({ @PrimaryKeyJoinColumn(name="…", referencedColumnName="…"), …})

@Table(name="TEIL")
public class Teil implements Serializable {

     //...

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "teil")
    private List<RelTeilWert> relTeilWert;



@Entity
@Table(name = "REL_TEIL_WERT")
public class RelTeilWert implements Serializable {

    //....

    @JoinColumns({
        @JoinColumn(name="FUE_GR_ID", referencedColumnName="FUE_GR_ID", insertable=false, updatable=false),
        @JoinColumn(name="VARIANTE_ID", referencedColumnName="VARIANTE_ID", insertable=false, updatable=false),
        @JoinColumn(name="TEIL_ID", referencedColumnName="TEIL_ID", insertable=false, updatable=false)
    })
    @ManyToOne
    private Teil teil;```

Super.

In dem Zusammenhang noch erwähnenswert: embedded IDs braucht man, wenn zusammengesetzte Primary Keys existieren, und die sind meist fachlicher Natur.
Modelliert man das mit technischen PKs, lässt sich das Ganze viel einfacher handhaben.

Auch sollte man überlegen, ob die ManyToOne-Beziehung überhaupt navigierbar sein muss; wenn nicht, dann braucht das auch nicht gemappt werden, das vereinfacht die Sache dann auch.