Me he encontrado con una situación (que creo que es extraña pero posiblemente bastante normal) en la que uso EntityManager.getReference (LObj.getClass (), LObj.getId ()) para obtener una entidad de base de datos y luego pasar el objeto devuelto a persistir en otra mesa.
Entonces, básicamente, el flujo fue así:
class TFacade { createT (FObj, AObj) { T TObj = nuevo 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 = nuevo FLH (); FLHObj.setF (FObj); FLHObj.setL (LObj); .... EntityManager.persist (FLHObj); ... } }
Recibía la siguiente excepción "java.lang.IllegalArgumentException: Entidad desconocida: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"
Después de mirarlo 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 estaba devolviendo un proxy.
Esto me hace preguntarme, ¿ cuándo 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, lo cual es muy conveniente en sí mismo. El método EntityManager.find () simplemente devuelve null si no puede encontrar la entidad.
Con respecto a los límites de la transacción, me parece que necesitaría usar el método find () antes de pasar la entidad recién encontrada 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.
fuente
Respuestas:
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 de estado (me refiero al método del setter). Como debe saber, getReference devuelve un objeto proxy que usa una característica poderosa llamada verificación automática sucia. Suponga lo siguiente
Si llamo al método de búsqueda , el proveedor de JPA, detrás de escena, llamará
Si llamo al método getReference , el proveedor de JPA, detrás de escena, llamará
Y usted sabe por qué ???
Cuando llame a getReference, obtendrá un objeto proxy. Algo como este (el proveedor de JPA se encarga de implementar este proxy)
Entonces, antes de confirmar la transacción, el proveedor de JPA verá el indicador stateChanged para actualizar O NO la entidad de la persona. Si no se actualiza ninguna fila después de la declaración de actualización, el proveedor de JPA lanzará EntityNotFoundException de acuerdo con la especificación de JPA.
Saludos,
fuente
SELECT
antesUPDATE
, sin importar cuál defind()
/getReference()
yo use. Lo que es peor,SELECT
atraviesa relaciones NON-LAZY (emitiendo nuevasSELECTS
), aunque solo quiero actualizar un solo campo en una entidad.Como expliqué en este artículo , asumiendo que tiene una
Post
entidad padre y un hijoPostComment
como se ilustra en el siguiente diagrama:Si llama
find
cuando intenta establecer la@ManyToOne
post
asociación:Hibernate ejecutará las siguientes declaraciones:
La consulta SELECT es inútil esta vez porque no necesitamos que se obtenga la entidad Post. Solo queremos establecer la columna de clave externa post_id subyacente.
Ahora, si usa
getReference
en su lugar:Esta vez, Hibernate emitirá solo la declaración INSERT:
A diferencia de
find
, elgetReference
único devuelve un proxy de entidad que solo tiene el identificador establecido. Si accede al Proxy, la instrucción SQL asociada se activará siempre que EntityManager esté todavía abierto.Sin embargo, en este caso, no necesitamos acceder a la entidad Proxy. Solo queremos propagar la clave externa al registro de la tabla subyacente, por lo que cargar un proxy es suficiente para este caso de uso.
Al cargar un Proxy, debe tener en cuenta que se puede lanzar una LazyInitializationException si intenta acceder a la referencia de Proxy después de cerrar EntityManager. Para obtener más detalles sobre el manejo de
LazyInitializationException
, consulte este artículo .fuente
hibernate.jpa.compliance.proxy
propiedad de configuración , por lo que puede elegir el cumplimiento de JPA o un mejor rendimiento de acceso a datos.getReference
es necesario si es suficiente para establecer una nueva instancia de modelo con el conjunto de PK. ¿Qué me estoy perdiendo?Debido a que una referencia está 'administrada', pero no hidratada, también puede permitirle eliminar una entidad por ID, sin necesidad de cargarla primero en la memoria.
Como no puede eliminar una entidad no administrada, es simplemente una tontería cargar todos los campos usando find (...) o createQuery (...), solo para eliminarlo inmediatamente.
fuente
EntityManager.getReference()
es realmente un método propenso a errores y realmente hay muy pocos casos en los que un código de cliente necesite usarlo.Personalmente, nunca necesité usarlo.
EntityManager.getReference () y EntityManager.find (): no hay diferencia en términos de gastos generales
No estoy de acuerdo con la respuesta aceptada y en particular:
No es el comportamiento que obtengo con Hibernate 5 y el javadoc de
getReference()
no dice tal cosa:EntityManager.getReference()
reserva una consulta para recuperar la entidad en dos casos:1) si la entidad está almacenada en el contexto de persistencia, esa es la caché de primer nivel.
Y este comportamiento no es específico de
EntityManager.getReference()
,EntityManager.find()
también ahorrará una consulta para recuperar la entidad si la entidad está almacenada en el contexto de persistencia.Puede comprobar el primer punto con cualquier ejemplo.
También puede confiar en la implementación real de Hibernate.
De hecho, se
EntityManager.getReference()
basa en elcreateProxyIfNecessary()
método de laorg.hibernate.event.internal.DefaultLoadEventListener
clase para cargar la entidad.Aquí está su implementación:
La parte interesante es:
2) Si no manipulamos eficazmente la entidad, haciéndonos eco de la búsqueda perezosa del javadoc.
De hecho, para garantizar la carga efectiva de la entidad, se requiere invocar un método en ella.
Entonces, ¿la ganancia estaría relacionada con un escenario en el que queremos cargar una entidad sin tener la necesidad de usarla? En el marco de las aplicaciones, esta necesidad es realmente poco común y además el
getReference()
comportamiento también es muy engañoso si lees la siguiente parte.Por qué favorecer EntityManager.find () sobre EntityManager.getReference ()
En cuanto a gastos generales,
getReference()
no es mejor quefind()
lo comentado en el punto anterior.Entonces, ¿por qué usar uno u otro?
La invocación
getReference()
puede devolver una entidad obtenida de forma perezosa.Aquí, la búsqueda diferida no se refiere a las relaciones de la entidad, sino a la entidad misma.
Significa que si invocamos
getReference()
y luego se cierra el contexto de persistencia, es posible que la entidad nunca se cargue y, por lo tanto, el resultado es realmente impredecible. Por ejemplo, si el objeto proxy está serializado, puede obtener unanull
referencia como resultado serializado o si se invoca un método en el objeto proxy,LazyInitializationException
se lanza una excepción como .Significa que el lanzamiento de
EntityNotFoundException
eso es la razón principal a utilizargetReference()
para manejar una instancia que no existe en la base de datos, ya que es posible que nunca se realice una situación de error mientras la entidad no exista.EntityManager.find()
no tiene la ambición de lanzarEntityNotFoundException
si no se encuentra la entidad. Su comportamiento es simple y claro. Nunca te sorprenderás ya que devuelve siempre una entidad cargada onull
(si no se encuentra la entidad) pero nunca una entidad bajo la forma de un proxy que puede no estar efectivamente cargada.Por tanto,
EntityManager.find()
debería favorecerse en la mayoría de los casos.fuente
No estoy de acuerdo con la respuesta seleccionada y, como Davidxxx señaló correctamente, getReference no proporciona tal comportamiento de actualizaciones dinámicas sin select. Hice una pregunta sobre la validez de esta respuesta, vea aquí: no se puede actualizar sin emitir select sobre el uso de setter después de getReference () de hibernate JPA .
Honestamente, no he visto a nadie que haya usado esa funcionalidad. EN CUALQUIER SITIO. Y no entiendo por qué está tan votada.
Ahora, en primer lugar, no importa lo que llame a un objeto proxy de hibernación, un setter o un getter, se dispara un SQL y se carga el objeto.
Pero luego pensé, ¿qué pasa si el proxy JPA getReference () no proporciona esa funcionalidad? Puedo escribir mi propio proxy.
Ahora, todos podemos argumentar que las selecciones en claves primarias son tan rápidas como puede ser una consulta y no es algo que deba evitarse mucho. Pero para aquellos de nosotros que no podemos manejarlo por una razón u otra, a continuación se muestra una implementación de dicho proxy. Pero antes de que vea la implementación, vea su uso y lo simple que es de usar.
USO
Y esto dispararía la siguiente consulta:
e incluso si desea insertar, aún puede hacer PersistenceService.save (new Order ("a", 2)); y dispararía un inserto como debería.
IMPLEMENTACIÓN
Agregue esto a su pom.xml -
Haga esta clase para crear un proxy dinámico -
Crea una interfaz con todos los métodos:
Ahora, cree un interceptor que le permitirá implementar estos métodos en su proxy:
Y la clase de excepción -
Un servicio para ahorrar usando este proxy -
fuente