JPA mit JTA: Entität beibehalten und kaskadierte untergeordnete Entitäten zusammenführen

Ich habe eine bidirektionale Eins-zu-Viele-Beziehung zu den folgenden Entitätsklassen:

0 oder 1 Kunde <-> 0 oder mehr Produktbestellungen

Wenn die Client-Entität beibehalten werden soll, sollen die zugeordneten Produktbestell-Entitäten ebenfalls beibehalten werden (da der Fremdschlüssel für den "übergeordneten" Client möglicherweise aktualisiert wurde).

Selbstverständlich werden alle erforderlichen CASCADE-Optionen clientseitig eingestellt. AberDies funktioniert nicht, wenn ein neu erstellter Client zum ersten Mal beibehalten wird, während auf eine vorhandene Produktbestellung verwiesen wird wie in diesem Szenario:

Produktauftrag '1' wird angelegt und bleibt bestehen. Funktioniert gut.Der Kunde '2' wird erstellt und die Produktbestellung '1' wird zu seiner Produktbestellungsliste hinzugefügt. Dann bleibt es bestehen. Funktioniert nicht.

Ich habe mehrere Ansätze ausprobiert, aber keiner von ihnen zeigte das erwartete Ergebnis. Siehe diese Ergebnisse unten. Ich habe alle zugehörigen Fragen hier gelesen, aber sie haben mir nicht geholfen. Ich verwende EclipseLink 2.3.0, reine JPA 2.0-Anmerkungen und JTA als Transaktionstyp für eine speicherinterne Apache Derby (JavaDB) -DB unter GlassFish 3.1.2. Entitätsbeziehungen werden von einer JSF-GUI verwaltet. Das Beziehungsmanagement auf Objektebene funktioniert (abgesehen davon, dass es weiterhin besteht). Ich habe es mit JUnit-Tests getestet.

Ansatz 1) "Standard" (basierend auf NetBeans-Klassenvorlagen)

Klient:

@Entity
public class Client implements Serializable, ParentEntity {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(mappedBy = "client", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH},
            fetch= FetchType.LAZY)
    private List<ProductOrder> orders = new ArrayList<>();

    // other fields, getters and setters
}

ProduktBestellung:

@Entity
public class ProductOrder implements Serializable, ChildEntity {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne // owning side
    private Client client;

    // other fields, getters and setters
}

Generic Persistence Facade:

// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
    getEntityManager().persist(entity);
}

// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
    getEntityManager().merge(entity);
}

Ergebnis:

create () löst diese Ausnahme sofort aus:

Warnung: Während eines Aufrufs der EJB ClientFacade-Methode public ist eine Systemausnahme aufgetreten. Void javaee6test.beans.AbstractFacade.create (java.lang.Object) javax.ejb.EJBException: Transaktion abgebrochen ...

Auslöser: javax.transaction.RollbackException: Transaktion für Rollback markiert. ...

Auslöser: Ausnahme [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException Interne Ausnahme: java.sql.SQLIntegrityConstraintViolationException: Die Anweisung wurde abgebrochen, da dies der Fall war haben einen doppelten Schlüsselwert in einer eindeutigen oder Primärschlüsseleinschränkung oder einem eindeutigen Index verursacht, der durch 'SQL120513133540930' in 'PRODUCTORDER' definiert ist. Fehlercode: -1 Aufruf: INSERT INTO PRODUCTORDER (ID, CLIENT_ID) VALUES (?,?) Bind => [2 Parameter gebunden] Abfrage: InsertObjectQuery (javaee6test.model.ProductOrder [id = 1]) ...

Auslöser: java.sql.SQLIntegrityConstraintViolationException: Die Anweisung wurde abgebrochen, weil sie einen doppelten Schlüsselwert in einer eindeutigen oder Primärschlüsseleinschränkung oder einem eindeutigen Index verursacht hätte, der durch 'SQL120513133540930' in 'PRO-DUCTORDER' definiert wurde. ...

Auslöser: org.apache.derby.client.am.SqlException: Die Anweisung wurde abgebrochen, da sie einen doppelten Schlüsselwert in einer eindeutigen oder Primärschlüsseleinschränkung oder einem eindeutigen Index verursacht hätte, der durch "SQL120513133540930" gekennzeichnet ist. PRODUCTOR-DER '.

Ich verstehe diese Ausnahme nicht. edit () funktioniert gut. ABER ich möchte einem Kunden zum Zeitpunkt seiner Erstellung Produktbestellungen hinzufügen, dies ist also nicht ausreichend.

Ansatz 2) nur merge ()

Änderungen an der allgemeinen Persistenz-Fassade:

// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
    getEntityManager().merge(entity);
}

// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
    getEntityManager().merge(entity);
}

Ergebnis:

Bei create () lautet die Ausgabe der EclipseLink-Protokollierung:

Fein: INSERT INTO CLIENT (ID, NAME, ADDRESS_ID) WERTE (?,?,?) Binden => [3 Parameter gebunden]

aber KEIN "UPDATE" in der Produktbestelltabelle. Somit ist die Beziehung nicht hergestellt. Auch hier funktioniert edit () einwandfrei.

Ansatz 3) ID GenerationType.IDENTITY für beide Entitätstypen

Änderungen sowohl an der Kunden- als auch an der Produktbestellklasse:

...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...

Ergebnis:

Bei create () lautet die Ausgabe der EclipseLink-Protokollierung:

Fein: INSERT INTO CLIENT (NAME, ADDRESS_ID) WERTE (?,?) Binden => [2 Parameter gebunden]

Fein: Werte IDENTITY_VAL_LOCAL ()

Fine: INSERT INTO PRODUCTORDER (ORDERDATE, CLIENT_ID) VALUES (?,?) Bind => [2 Parameter gebunden]

Fein: Werte IDENTITY_VAL_LOCAL ()

Anstatt also eine Beziehung zu der Produktbestellung herzustellen, die der Kundenliste hinzugefügt wurde, wird eine neue Produktbestellungsentität erstellt und beibehalten (!), und eine Beziehung zu dieser Entität wird hergestellt. Gleiches gilt für edit ().

Ansatz 4) Ansatz (2) und (3) kombiniert

Ergebnis: Wie Ansatz (2).

Meine Frage ist:Gibt es eine Möglichkeit, das oben beschriebene Szenario zu realisieren? Wie kann es aufgebaut werden? Ich möchte bei JPA bleiben (keine herstellerspezifische Lösung).

Antworten auf die Frage(4)

Ihre Antwort auf die Frage