2009-10-22 32 views
83

Me he encontrado con una situación (que creo que es extraña pero es bastante normal) en la que utilizo EntityManager.getReference (LObj.getClass(), LObj .getId()) para obtener una entidad de base de datos y luego pasar el objeto devuelto para que se conserve en otra tabla.Cuándo utilizar EntityManager.find() vs EntityManager.getReference()

Así que, básicamente, el flujo fue de esta manera:

 
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); 
    ... 
    } 
} 

que estaba recibiendo la siguiente excepción "java.lang.IllegalArgumentException: entidad desconocida: com.my.persistence.L $$ $$ EnhancerByCGLIB 3e7987d0"

Después de verlo por un tiempo, finalmente descubrí que era porque estaba usando el método EntityManager.getReference() que estaba obteniendo la excepción anterior ya que el método devolvía un proxy.

Esto hace que me pregunte, cuando es aconsejable utilizar el método EntityManager.getReference() en lugar del método EntityManager.find()?

EntityManager.getReference() arroja una EntityNotFoundException si no puede encontrar la entidad que se busca, que es muy conveniente en sí misma. El método EntityManager.find() simplemente devuelve null si no puede encontrar la entidad.

Con respecto a los límites de transacción, me parece que necesitaría usar el método find() antes de pasar la entidad recién descubierta a una nueva transacción. Si usa el método getReference(), probablemente terminaría en una situación similar a la mía con la excepción anterior.

+0

Ha olvidado mencionar que estoy utilizando Hibernate como proveedor de JPA. – SibzTer

Respuesta

133

Normalmente uso el método getReference cuando no necesito acceder al estado de la base de datos (me refiero al método getter). Solo para cambiar el estado (me refiero al método setter). Como deberías saber, getReference devuelve un objeto proxy que utiliza una poderosa función llamada comprobación automática sucia. Supongamos la siguiente

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); 
    } 

} 

si llamo encontrar método, proveedor JPA, detrás de las escenas, se llame

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ? 

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

si llamo getReference método, proveedor JPA, detrás de las escenas, la voluntad llamar

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ? 

¿Y sabes por qué ???

Cuando llame a getReference, obtendrá un objeto proxy. Algo así como este (proveedor JPA se encarga de la aplicación de este proxy)

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; 
    } 

} 

Así que antes de confirmación de transacción, proveedor JPA verá bandera stateChanged con el fin de actualizar O NO entidad persona. Si no se actualizan filas después de la declaración de actualización, el proveedor de JPA arrojará EntityNotFoundException de acuerdo con la especificación de JPA.

cordiales,

+0

Estoy usando EclipseLink 2.5.0 y las consultas indicadas anteriormente no son correctas. Siempre emite un 'SELECT' antes de' UPDATE', sin importar cuál de 'find()'/'getReference()' Yo uso. Lo que es peor, 'SELECT' atraviesa relaciones NON-LAZY (emitiendo nuevos' SELECTS'), aunque solo quiero actualizar un solo campo en una entidad. –

+0

@Arthur Ronald ¿qué sucede si hay una anotación de Versión en la entidad llamada por getReference? –

+0

Tengo el mismo problema que @DejanMilosevic: al eliminar una entidad obtenida a través de getReference(), se emite un SELECT en esa entidad y atraviesa todas las relaciones LAZY de esa entidad, emitiendo así muchos SELECTS (con EclipseLink 2.5.0). –

5

Puesto que una referencia se 'arregló', pero no hidratado, sino que también pueden permitirá eliminar una entidad por ID, sin necesidad de cargarlo en la memoria primero.

Como no puede eliminar una entidad no administrada, es simplemente absurdo cargar todos los campos usando find (...) o createQuery (...), solo para eliminarlo de inmediato.

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId); 
em.remove(myObject); 
Cuestiones relacionadas