PDA

View Full Version : Hibernate-Entity mit Verweisen aus mehreren zusammengesetzten Schlüssel



mwithake
26-11-10, 11:00
Hallo,

ich versuche micht derzeit an Hibernate und möchte gerne unsere Datenbank mit Hibernate mappen.

Dabei stosse ich auf das Problem, das wir verweise auf andere Entitäten haben (@ManyToOne), die aus mehreren zusammengesetzten Schlüsselfelder bestehen. Dabei teilen sich die Verweise einzelnen Spalten aus der Tabelle. Als Beispiel Kunde-Id und Versandadresse-Id auf Versandadresse und Kunde-Id und Ansprechpartern-Id auf Ansprechpartern wobei sich beide Relationen die Kunden-Id teilen sowie Kunden-Id noch selber für die Kunden-Entität.

Bei Hibernate habe ich nun das Problem, das ich Abbrüche bekomme:
"Repeated column in mapping for entity: ft.Kommission column: aekdn (should be mapped with insert="false" update="false")"

Wenn ich das @JoinColumn auf insertable=false und updateable=false setze, funktioniert zwar der Abruf aber ich kann z.B. den Ansprechpartner nicht mehr update bzw. schreiben.

Kennt jemand das Problem, gibt es ein Möglichkeit das richtig umzusetzen?

Gruß
Martin

RobertPic
29-11-10, 23:41
Kennt jemand das Problem...
Ja, ein typisches Problem mit Legacydateien. Wobei die meisten Verknüpfungen im Normalfall die verknüpfte Datei ja nicht mitändern sollen. Also z.B. wenn zu den Aufträgen die Kunden verknüpft sind, ist die "Read only" Verknüpfung kein Problem.

Um das noch einmal zu verdeutlichen, die nachfolgenden Erweiterungen brauchst du nur, wenn du auch den Inhalt der verknüpften Daten ändern willst. Die Schlüsselfelder werden auch bei insert/update=false mitgespeichert.

Beispiel: Aus einer Listbox wird eine der möglichen Versandadressen ausgewählt und zugewiesen. Damit werden die Id's beschickt und diese werden auch gespeichert.



..gibt es ein Möglichkeit das richtig umzusetzen?
Am schönsten sind neue Dateien mit Id's als Schlüssel bzw. Fremdschlüssel.

Eine weitere Möglichkeit sind Surrogate Keys, also zusätzliche Id's für den "Dualbetrieb" (alte und neue Programme). Hier der entsprechende Wiki Eintrag (http://de.wikipedia.org/wiki/Surrogatschl%C3%BCssel).

Wenn man die Dateien nicht erweitern bzw. ändern darf/kann, bietet es sich diese fixen Verknüpfungen in der Service/DAO-Schicht zu implementieren.
Also z.B. im AuftragService, Methode
persistAuftrag(Auftrag auftrag)
wird auch die abweichende Adresse gespeichert.

mwithake
30-11-10, 07:16
Ja, ein typisches Problem mit Legacydateien. Wobei die meisten Verknüpfungen im Normalfall die verknüpfte Datei ja nicht mitändern sollen. Also z.B. wenn zu den Aufträgen die Kunden verknüpft sind, ist die "Read only" Verknüpfung kein Problem.

Um das noch einmal zu verdeutlichen, die nachfolgenden Erweiterungen brauchst du nur, wenn du auch den Inhalt der verknüpften Daten ändern willst. Die Schlüsselfelder werden auch bei insert/update=false mitgespeichert.

Beispiel: Aus einer Listbox wird eine der möglichen Versandadressen ausgewählt und zugewiesen. Damit werden die Id's beschickt und diese werden auch gespeichert.



Am schönsten sind neue Dateien mit Id's als Schlüssel bzw. Fremdschlüssel.

Eine weitere Möglichkeit sind Surrogate Keys, also zusätzliche Id's für den "Dualbetrieb" (alte und neue Programme). Hier der entsprechende Wiki Eintrag (http://de.wikipedia.org/wiki/Surrogatschl%C3%BCssel).

Wenn man die Dateien nicht erweitern bzw. ändern darf/kann, bietet es sich diese fixen Verknüpfungen in der Service/DAO-Schicht zu implementieren.
Also z.B. im AuftragService, Methode
persistAuftrag(Auftrag auftrag)
wird auch die abweichende Adresse gespeichert.


Bist Du sicher das die Schlüsselfelder auch bei insert/update=false mitgespeichert werden? Nur die Schlüsselfelder zu speichern ist genau das, was ich möchte. Ich habe das ausprobiert und es ist leider nicht so. Oder mache ich etwas falsch. Hier mal die Source:


package ft;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.Table;
import javax.persistence.Transient;
import buchhaltung._incubator.Kostenstelle;
import core.Firma;
@Entity
@Table(name="aekomp")
public class Kommission implements Serializable{

@EmbeddedId
private DatabaseId id;

@MapsId("firma_id")
@JoinColumn(name="aefa")
@ManyToOne(fetch=FetchType.LAZY)
private Firma firma;

@Column(name="aelus")
private String bezeichnung;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns({@JoinColumn(name="aefa", referencedColumnName="kofa", updatable=false, insertable=false),
@JoinColumn(name="aekst", referencedColumnName="kost", updatable=false, insertable=false)})
private Kostenstelle kostenstelle;


@Embeddable
public static class DatabaseId implements Serializable{

@Column(name="aefa")
int firma_id;
@Column(name="aekom")
int kommissions_id;

public DatabaseId() {
}

public DatabaseId(int firmaId, int kommissionsId) {
super();
firma_id = firmaId;
kommissions_id = kommissionsId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + firma_id;
result = prime * result + kommissions_id;
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DatabaseId other = (DatabaseId) obj;
if (firma_id != other.firma_id)
return false;
if (kommissions_id != other.kommissions_id)
return false;
return true;
}


}
public Firma getFirma() {
return firma;
}
public void setFirma(Firma firma) {
this.firma = firma;
}
public Kostenstelle getKostenstelle() {
return kostenstelle;
}
public void setKostenstelle(Kostenstelle kostenstelle) {
this.kostenstelle = kostenstelle;
}


public String getBezeichnung() {
return bezeichnung;
}
public void setBezeichnung(String bezeichnung) {
this.bezeichnung = bezeichnung;
}
@Override
public String toString() {
return id.kommissions_id+" "+getBezeichnung();
}
}


Wenn ich das folgendes ausführe:



kommission.setKostenstelle(kostenstelle);
kommission.setBezeichnung("TEST");


session.update(kommission);

session.flush();


Erstellt Hibernate folgende Anweisung:



Hibernate: update aekomp set aelus=? where aefa=? and aekom=?


Wobei die Kostenstelle (aekst) nicht aktualisiert wird

RobertPic
30-11-10, 14:27
Bist Du sicher das die Schlüsselfelder auch bei insert/update=false mitgespeichert werden?Nein, da habe etwas zu schnell drübergeschaut. Ich habe meine Beans etwas "schlauer" gemacht. Wenn ein Objekt mit update/insert=false zugewiesen wird, erfolgt das zuweisen der Einzelwerte manuell.

Also bei z.B.


public void setKostenstelle(Kostenstelle kostenstelle) {

this.kostenstelle = kostenstelle;
this.id.firma_id = kostenstell.getFirma_id();
...
}
Damit brauchen sich die weiteren Programme nicht mehr um diesen Umstand kümmern und diverse GUI-Builder funktionieren damit dann auch.