2009-10-22 13 views
83

Mi sono imbattuto in una situazione (che a mio avviso è strana ma è probabilmente abbastanza normale) in cui utilizzo EntityManager.getReference (LObj.getClass(), LObj .getId()) per ottenere un'entità database e quindi passare l'oggetto restituito da mantenere in un'altra tabella.Quando utilizzare EntityManager.find() vs EntityManager.getReference()

Quindi, in pratica il flusso era così:

 
class TFacade{ 

    createT(FObj, AObj) { 
    T TObj = new T(); 
    TObj.setF(FObj); 
    TObj.setA(AObj); 
    ... 
    EntityManager.persist(TObj); 
    ... 
    L LObj = A.getL(); 
    FObj.setL(LObj); 
    FFacade.editF(FObj); 
    } 
} 

@TransactionAttributeType.REQUIRES_NEW 
class FFacade{ 

    editF(FObj){ 
    L LObj = FObj.getL(); 
    LObj = EntityManager.getReference(LObj.getClass(), LObj.getId()); 
    ... 
    EntityManager.merge(FObj); 
    ... 
    FLHFacade.create(FObj, LObj); 
    } 
} 

@TransactionAttributeType.REQUIRED 
class FLHFacade{ 

    createFLH(FObj, LObj){ 
    FLH FLHObj = new FLH(); 
    FLHObj.setF(FObj); 
    FLHObj.setL(LObj); 
    .... 
    EntityManager.persist(FLHObj); 
    ... 
    } 
} 

mi è stato sempre la seguente eccezione "java.lang.IllegalArgumentException: un'entità sconosciuta: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"

Dopo averlo esaminato per un po ', ho finalmente capito che era perché stavo usando il metodo EntityManager.getReference() che stavo ottenendo l'eccezione di cui sopra come il metodo stava restituendo un proxy.

Questo mi fa chiedere, quando è opportuno utilizzare il metodo invece del metodo EntityManager.find() EntityManager.getReference()?

EntityManager.getReference() genera un'entitàNotFoundException se non riesce a trovare l'entità da cercare, il che è molto conveniente in sé. Il metodo EntityManager.find() restituisce semplicemente null se non riesce a trovare l'entità.

Per quanto riguarda i confini delle transazioni, mi sembra che sarebbe necessario utilizzare il metodo find() prima di passare l'entità appena trovata a una nuova transazione. Se si utilizza il metodo getReference(), si rischia di finire in una situazione simile alla mia con l'eccezione di cui sopra.

+0

Ho dimenticato di menzionare, sto usando Hibernate come provider JPA. – SibzTer

risposta

133

Io di solito uso il metodo getReference quando non ho bisogno di accedere allo stato del database (intendo il metodo getter). Solo per cambiare stato (intendo metodo setter). Come dovresti sapere, getReference restituisce un oggetto proxy che utilizza una potente funzione chiamata controllo sporco automatico. Supponiamo che il seguente

public class Person { 

    private String name; 
    private Integer age; 

} 


public class PersonServiceImpl implements PersonService { 

    public void changeAge(Integer personId, Integer newAge) { 
     Person person = em.getReference(Person.class, personId); 

     // person is a proxy 
     person.setAge(newAge); 
    } 

} 

se chiamo trovare metodo, provider JPA, dietro le quinte, chiamerà

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? 

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

se chiamo getReference metodo, provider JPA, dietro le quinte, la volontà chiama

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

E sai perché ???

Quando si chiama getReference, si ottiene un oggetto proxy. Qualcosa di simile a questo (provider JPA si prende cura di applicazione del presente delega)

public class PersonProxy { 

    // JPA provider sets up this field when you call getReference 
    private Integer personId; 

    private String query = "UPDATE PERSON SET "; 

    private boolean stateChanged = false; 

    public void setAge(Integer newAge) { 
     stateChanged = true; 

     query += query + "AGE = " + newAge; 
    } 

} 

Quindi, prima operazione di commit, provider JPA vedrà stateChanged bandiera al fine di aggiornare o non persona entità. Se nessuna riga viene aggiornata dopo l'istruzione di aggiornamento, il provider JPA genererà EntityNotFoundException in base alle specifiche JPA.

saluti,

+0

Sto usando EclipseLink 2.5.0 e le domande sopra indicate non sono corrette. Emette sempre un 'SELECT' prima di 'UPDATE', non importa quale di' find() '/' getReference() 'Io uso. Quel che è peggio, 'SELECT' attraversa le relazioni NON-LAZY (emettendo nuovi' SELECTS'), anche se voglio solo aggiornare un singolo campo in una entità. –

+0

@Arthur Ronald cosa succede se c'è un'annotazione della versione nell'entità chiamata da getReference? –

+0

Ho lo stesso problema di @DejanMilosevic: quando rimuovi un'entità ottenuta tramite getReference(), viene emesso un SELECT su quell'entità e attraversa tutte le relazioni LAZY di quell'entità, emettendo così molti SELEZIONATI (con EclipseLink 2.5.0). –

5

Poiché un riferimento viene 'gestito', ma non idratata, può anche permettere di rimuovere un'entità da ID, senza bisogno di caricare in memoria prima.

Come non è possibile rimuovere un'entità non gestita, è semplicemente sciocco caricare tutti i campi utilizzando find (...) o createQuery (...), solo per eliminarlo immediatamente.

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId); 
em.remove(myObject); 
Problemi correlati