2013-07-23 7 views
6

Ho un'entità che è stata precedentemente persa e ha una relazione @OneToMany con un'altra entità. Per aggiungere una nuova entità, aggiungo la mia nuova entità nell'oggetto gestito e utilizzo cascadeType.ALL per mantenere le modifiche. C'è un modo per ottenere gli id ​​degli oggetti appena creati o ottenere l'oggetto originale (non gestito) che ho usato con l'unione per avere il suo id aggiornato?JPA Come posso ottenere l'id/oggetto generato quando si usa l'unione da padre ma viene creato un figlio?

In pseudo-codice che ci si aspetta quanto segue per accadere:

  1. Nuova copia sta per essere restituito per entità risultante dalla fusione
  2. vecchia copia sarà aggiornamento per le nuove entità

Esempio: Parent a, id = 13 Bambino B, id = 0

In sostanza, voglio emettere un merge sul genitore, ma a cascata persist sul figlio (in modo che l'istanza figlio originale sia aggiornata, non copiata).

Ovviamente questo non accade. Sto utilizzando la sospensione come provider.

+0

Quale database, provider JPA e strategia di generazione id stai usando? Con Oracle 11g + Hibernate + '@ SequenceGenerator' gli oggetti si sovrappongono perfettamente e ritorna con il nuovo ID assegnato. –

+0

Sto usando MySql e '@ GeneratedValue'. Non sono un fan degli id ​​aggregati che ritengo sia il motivo per cui funziona per Oracle/'SequenceGenerator' –

+0

@johnd Ho apportato delle modifiche alla domanda nel tentativo di chiarire; se ho frainteso, per favore, rispondi alle mie modifiche. –

risposta

6

StackOverflow post e JPA documentation avere la risposta a condizione che fate la vostra ricerca.

Il modo di fare ciò che voglio è utilizzare persist su gestito da genitore. Questo ignorerà tutte le modifiche sul genitore, ma sarà in cascata persist (a condizione che sia impostato su cascata). In seguito l'oggetto figlio avrà l'id corretto.

.... 
JPAEntity newObject=new JPAEntity(); 
managedObject.addChild(newObject); 
em.persist(managedObject) 
newObject.getId() //work fine! 
1

Si dovrebbe essere in grado di "vedere" un ID generato per una nuova entità:

  • dopo commit della transazione, o

  • dopo un em.flush() (dove em è il vostro EntityManager) mentre una transazione è attivo.

Si noti inoltre che tutte le relazioni tra le entità devono essere risolti in Java strutture di dati prima della persistenza. I riferimenti dei bambini ai genitori devono essere "impostati" e viceversa.

+0

Se eseguo il loop dell'entità restituita (unita), posso vedere che l'ID è stato impostato, nessun problema. Ma non riesco a vedere un id sull'originale (oggetto child/non persistente). –

+3

L'operazione di unione crea una nuova * copia gestita * dell'oggetto, il gestore dell'entità non traccia, modifica o altrimenti "gestisce" la copia originale non gestita. Come capisco la tua domanda, sei sfortunato. –

0

Assicurarsi di impostare entrambi i siti della relazione prima di mantenere le modifiche.

child.setParent(parent); 
parent.getChildren().add(child); 
Parent parentWithId = em.merge(parent); 
em.flush(); // make sure that the persistence context and database are in sync 
parentWithId.getId(); // works 
parentWithId.getChildren().get(0).getId(); // should also work 
+0

Sto impostando entrambi i lati della relazione e sì, funziona sopra. Ma io non voglio attraversare la collezione per trovare il nuovo id (I am atm, ma non mi piace). Esistono altre soluzioni alternative, ma mi aspetterei una soluzione più diretta. –

+0

jonh, questo non è quello che hai chiesto originariamente. Se vuoi lavorare con l'oggetto originale, usa 'persist' su parent, flush e refresh. http://stackoverflow.com/questions/4870863/why-jpa-persist-does-not-generated-auto-increment-primary-id –

1

Ho avuto un problema simile con il fornitore di persistenza eclipselink. Avevo bisogno di aggiungere nuovi ordini da un database client con "client orderIDs" temporanei a un cliente esistente in un database del server. Poiché avevo bisogno di "orderIDs del server" per ulteriori richieste del client, volevo creare una mappa (clientID, serverID) e inviarla al client. Fondamentalmente il mio approccio è di ottenere un riferimento alla copia dell'ordine bambino appena aggiunto. Sarà sempre aggiunto alla fine della mia lista nel cliente della classe genitore, che viene utilizzato per recuperarlo.

Uso le classi di entità Cliente e Ordine, dove il Cliente ha una lista (LinkedList) di ordini.

Parent classe Cliente: ...

@OneToMany(mappedBy = "customer", cascade=CascadeType.ALL, orphanRemoval = true) 
    private List<Order> orders = new LinkedList<Order>(); 

    public Order getLastOrder() { 
     return orders.get(orders.size()-1); 
    } 

Bambino Order classe:

@ManyToOne(optional=false) 
    private Customer customer; 

Servlet:

Customer customer = customerService.findByID(customerID); //existing customer 
    Order order = new Order(); 
    //add attributes to new order 
    customer.addOrder(order); 
    customerService.update(customer); //generates keys for children 
    order = customer.getLastOrder(); 
    Long orderID = order.getID; //Nullpointer exception 

aggiornamenti CustomerService cliente con EntityManager.merge ma ho dimenticato di aggiornare il riferimento nel servlet:

classe CustomerService:

public void update(Customer entity) { 
     entityManager.merge(entity); 
    } 

ho risolto in questo modo: class CustomerService:

public Customer update(Customer entity) { 
     return entityManager.merge(entity); 
    } 

Servlet: ....

customer = customerService.update(customer); //generates keys for children 

Ora tutto funziona bene.

Problemi correlati