JPA Speichern komplexer Objekte

fsicher

Bekanntes Mitglied
Hallo allerseits

Ich habe das Problem beim Speichern eines komplexen Objekts, wenn ein Teil-Objekt aus der Db geholt wird, da zuvor schon gespeichert.

Folgendes (vereinfachtes) Beispiel:

Die Klasse Anrede:
Java:
@Entity
public class Anrede implements Serializabler {

	@Id
	@GeneratedValue
	private int id;
	private String bezeichnung;

Klasse Kunde:
Java:
public class Kunde implements Serializable{

	@Id
	@GeneratedValue
	private int id;

	@ManyToOne(cascade = CascadeType.ALL)
	private Anrede anrede;
	private String vorname;
	private String nachname;
	@OneToOne(cascade = CascadeType.ALL)
	private Adresse adresse;
	@OneToMany(mappedBy = "kunde", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	private final List<Vertrag> vertragListe = new ArrayList<Vertrag>();

    // ...

}

Klasse Vertrag:
Java:
@Entity
@Cacheable(false)
public class Vertrag implements Serializable{

	@Id
	@GeneratedValue
	private int id;

	@ManyToOne(cascade = CascadeType.ALL)
	private Kunde kunde;

	// ...

}

Wenn ich den Vertrag erzeuge, wobei auch ein neuer Kunde erstellt wird (also nicht aus der Db geholt), funktioniert das Speichern problemlos. Wenn ich aber für den Vertrag einen bestehenden Kunden haben möchte, funktioniert es nicht:

Java:
javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: FEHLER: doppelter Schlüsselwert verletzt Unique-Constraint »anrede_pkey«
  Detail: Schlüssel »(id)=(13)« existiert bereits.
Error Code: 0
Call: INSERT INTO ANREDE (ID, BEZEICHNUNG) VALUES (?, ?)
	bind => [2 parameters bound]
Query: InsertObjectQuery(Anrede [id=13, bezeichnung=Herr])
	at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
	at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)

... (stacktrace)

In ter Tabelle Anrede existiert ein Eintrag mit PK 13: dies gehört dem Kunde-Objekt, das ich aus der Db hole, um ihn mit dem Vertrag "zu verknüpfen".

Ich hätte erwartet, dass es erkannt wird, dass das Kunde-Objekt in der Db schon existiert (mit seiner Anrede) und demzufolge kein neuer Eintrag gemacht wird.

Was mache ich falsch hier? Muss ich jetzt noch das komplexe Objekt in einzelne Teile zerlegen und dann jeweil prüfen, ob das Objekt in der Db schon enthalten ist und je nach Resultat anders handeln? Macht das nicht der JPA-Provider selbst (Eclipse Link)? Oder, ist meine Beziehung falsch definiert?

Danke für jeden Tipp.
 

fsicher

Bekanntes Mitglied
Hat sich erledigt: da ich den Kunden aus der Db hole, muss ich "merge" aufrufen.

Java:
Kunde k = vertrag.getKunde();

if (k.getId() > 0){
    k = em.merge(k);
}

// Das reicht noch nicht! Ich muss noch die Zuweisung explizit machen (aus welchem Grund auch immer):
vertrag.setKunde(k);

Jetzt kommt aber das nächste Problem: in meinem Vertrag habe ich eine Liste, in der die Geräte verwaltet werden.

Java:
@OneToMany(mappedBy = "vertrag", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private final List<Geraet> geraeteListe = new ArrayList<Geraet>();

In der Klasse Geraet:
Java:
@Entity
public class Geraet implements Serializable {

	// ...

	@ManyToOne
	private ServiceVertrag vertrag;

    // ...

}
Sonst gibt es in der Klasse nur noch einfache Datentypen (String und int).

Ich erstelle einen Vertrag, hole mir den Kunden aus der Db, erstelle zwei Geräte und füge sie in die Liste ein, indem ich die Methode addGeraet der Klasse Vertrag aufrufe:

Java:
public void addGeraet(Geraet geraet) {
	if (!geraeteListe.contains(geraet)) {
		geraeteListe.add(geraet);

		if (geraet.getVertrag() != null) {
			geraet.getVertrag().getGeraeteListe().remove(geraet);
		}

		geraet.setVertrag(this);
	}
}

Geräte werden in die Liste eingefügt und die Referenzen auf den Vertrag in dem jeweiligen Gerät korrekt gesetzt. Wenn ich es speichern versuche, bekomme ich folgende Meldung:

Java:
javax.persistence.RollbackException: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: Vertrag [id=0, xxx ]
Geraet [id=26, xxx ]
].

Anschliessend wird rollback gemacht und das Gerät (das gespeichert wurde --> id = 26) aus der Db entfernt. Wenn ich keine Geräte der Liste hinzufüge (leere Liste), wird der Vertrag korrekt gespeichert.

Sieht jemand vielleicht, was hier falsch ist?

Danke.
 
Zuletzt bearbeitet:
N

nillehammer

Gast
Ändere die Klasse Vertrag wie folgt:
[JAVA=6]
@ManyToOne(cascade=CascadeType.ALL)
private ServiceVertrag vertrag;
[/code]
 

Ähnliche Java Themen


Oben