ho un rapporto bidirezionale uno-a-molti con le seguenti classi di entità:JPA con JTA: persist entità e unire entità figlio cascata
0 o 1 client < -> 0 o più ordini dei prodotti
Quando persiste l'entità client, desidero che anche le entità ordine prodotto associate siano mantenute (poiché la loro chiave esterna per il client "principale" potrebbe essere stata aggiornata).
Naturalmente tutte le opzioni CASCADE richieste sono impostate sul lato client. Ma non funziona se un cliente appena creato viene mantenuto per la prima volta, mentre fa riferimento a un ordine prodotto esistente come in questo scenario:
- ordine prodotto '1' è stato creato e persistente. Funziona bene.
- Il client '2' viene creato e l'ordine del prodotto '1' viene aggiunto all'elenco dei suoi ordini di prodotti. Quindi è persistito. Non funziona.
Ho provato diversi apporache, ma nessuno di loro ha mostrato il risultato previsto. Vedi quei risultati qui sotto. Ho letto tutte le domande correlate qui, ma non mi hanno aiutato. Uso EclipseLink 2.3.0, pure JPA 2.0 Annotations e JTA come tipo di transazione su un DB in memoria di Apache Derby (JavaDB) su GlassFish 3.1.2. Le relazioni di entità sono gestite da una GUI JSF. La gestione delle relazioni a livello di oggetto funziona (a parte il persistere), l'ho testato con i test JUnit.
Approccio 1) "Default" (sulla base di modelli di NetBeans classe)
Cliente:
@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
}
ProductOrder:
@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
}
Generico Persistenza Facciata:
// 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);
}
Risultato:
creare() genera questa eccezione immediatamente:
Attenzione: un'eccezione di sistema si è verificato nel corso di una chiamata su EJB metodo ClientFacade public void javaee6test.beans.AbstractFacade.create (java.lang .Object) javax.ejb.EJBException: Operazione interrotta ...
causato da: javax.transaction.RollbackException: transazione contrassegnata per il rollback. ...
Causato da: eccezione [EclipseLink-4002] (Eclipse Persistenza Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException interne Eccezione: java.sql. SQLIntegrityConstraintViolationException: lo stato è stato interrotto perché avrebbe causato un valore chiave duplicato in un vincolo di chiave univoco o primario o indice univoco identificato da "SQL120513133540930" definito su "PRODUCTORDER".Codice di errore: -1 Chiama: INSERISCI IN PRODUCTORDER (ID, CLIENT_ID) VALUES (?,?) Bind => [2 parametri associati] Query: InsertObjectQuery (javaee6test.model.ProductOrder [id = 1]) ...
causato da: java.sql.SQLIntegrityConstraintViolationException: La dichiarazione è stata interrotta perché avrebbe causato un valore di chiave duplicata in un unico o primaria vincolo di chiave o indice univoco identificato da 'SQL120513133540930' definito in 'PRO- DUCTORDER'. ...
causato da: org.apache.derby.client.am.SqlException: La dichiarazione è stata interrotta essere le cause che avrebbe causato un valore chiave duplicata in un unico o primario con-Straint tasto o indice univoco identificato da 'SQL120513133540930' definito su 'PRODUCTOR-DER'.
Non capisco questa eccezione. edit() funziona bene. Ma vorrei aggiungere ordini di prodotti a un cliente al momento della sua creazione, quindi questo è insufficiente.
Approccio 2) merge() solo
Modifiche a Generic Persistenza Facciata:
// 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);
}
Risultato:
su Create(), l'uscita di registrazione EclipseLink dice:
Fine: INSERIRE I VALORI DEL CLIENT (ID, NAME, ADDRESS_ID) (?, ?,?) bind => [3 parametri legati]
ma NO "UPDATE" sul tavolo ordine del prodotto. Quindi, la relazione non è stabilita. Di nuovo, edit(), d'altra parte, funziona bene.
suddetto approccio 3) Id GenerationType.IDENTITY su entrambi i tipi di entità
modifiche sia del cliente e l'ordine prodotto di classe:
...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
Risultato:
su Create(), l'EclipseLink Uscita di registrazione dice:
Fine: INSERT I NTO CLIENT (nome, address_id) VALORI legano => [2 parametri legati]
Belle (,??): Valori IDENTITY_VAL_LOCAL()
fine: INSERT INTO PRODUCTORDER (Data ordine, client_id) VALORI (? ,?) si legano => [2 parametri legati]
Belle: valori IDENTITY_VAL_LOCAL()
così invece di estabilshing una relazione con l'ordine del prodotto aggiunto alla lista del cliente, viene creata una nuova entità ordine prodcut e persistente (!) e viene stabilita una relazione con tale entità. Lo stesso qui, edit() funziona bene.
suddetto approccio 4) Approccio (2) e (3) combinato
Risultato: come Approach (2).
La mia domanda è: Esiste un modo per realizzare lo scenario sopra descritto? Come può essere ottenuto? Vorrei restare con JPA (nessuna soluzione specifica del fornitore).
No, questo non funziona. In realtà non cambia nulla. Ho provato con '@JoinColumn (name =" CLIENT_ID ", referencedColumnName =" ID ") ', ma non ha funzionato. – SputNick