Tengo una situación en la que necesito volver a adjuntar objetos separados a una sesión de hibernación, aunque un objeto de la misma identidad PUEDE ya existir en la sesión, lo que causará errores.
En este momento, puedo hacer una de dos cosas.
getHibernateTemplate().update( obj )
Esto funciona si y solo si un objeto no existe en la sesión de hibernación. Se lanzan excepciones indicando que un objeto con el identificador dado ya existe en la sesión cuando lo necesito más tarde.getHibernateTemplate().merge( obj )
Esto funciona si y solo si existe un objeto en la sesión de hibernación. Se producen excepciones cuando necesito que el objeto esté en una sesión posterior si uso esto.
Dados estos dos escenarios, ¿cómo puedo adjuntar sesiones genéricamente a los objetos? No quiero usar excepciones para controlar el flujo de la solución de este problema, ya que debe haber una solución más elegante ...
refresh()
en entidades separadas. Mirando a través de la especificación 2.0 no veo ninguna justificación; solo que no está permitido.*Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.
se puede encontrar más en la sección 9.3.2lock(LockMode.NONE)
de hecho se puede invocar en un objeto transitorio, y vuelve a conectar la entidad a la sesión. Ver stackoverflow.com/a/3683370/14379Todas estas respuestas pierden una distinción importante. update () se usa para (re) adjuntar su gráfico de objeto a una sesión. Los objetos que le pasas son los que se gestionan.
merge () en realidad no es una API (re) adjunta. Observe que merge () tiene un valor de retorno? Esto se debe a que le devuelve el gráfico administrado, que puede no ser el gráfico que le pasó. merge () es una API JPA y su comportamiento se rige por la especificación JPA. Si el objeto que pasa a merge () ya está administrado (ya asociado con la sesión), entonces ese es el gráfico con el que trabaja Hibernate; el objeto pasado es el mismo objeto devuelto por merge (). Sin embargo, si el objeto que pasa en merge () está separado, Hibernate crea un nuevo gráfico de objeto que se administra y copia el estado de su gráfico separado en el nuevo gráfico administrado. Nuevamente, todo esto está dictado y gobernado por la especificación JPA.
En términos de una estrategia genérica para "asegurarse de que esta entidad se administre, o hacer que se administre", depende de si desea tener en cuenta los datos aún no insertados. Suponiendo que lo haga, use algo como
Observe que usé saveOrUpdate () en lugar de update (). Si no desea que los datos aún no insertados se manejen aquí, use update () en su lugar ...
fuente
Session.contains(Object)
controles por referencia. Si ya hay otra entidad que representa la misma fila en la sesión y pasa una instancia separada, obtendrá una excepción.Session.contains(Object)
comprobaciones por referencia, si hay otra entidad que representa la misma fila en la sesión, devolverá falso y lo actualizará.Respuesta no diplomática: Probablemente esté buscando un contexto de persistencia extendido. Esta es una de las principales razones detrás de Seam Framework ... Si tiene dificultades para usar Hibernate en Spring en particular, consulte esta pieza de los documentos de Seam.
Respuesta diplomática: Esto se describe en los documentos de Hibernate . Si necesita más aclaraciones, eche un vistazo a la Sección 9.3.2 de Java Persistence with Hibernate llamada "Trabajar con objetos separados". Yo fuertemente recomiendo consigo este libro, si está haciendo algo más que CRUD con Hibernate.
fuente
Si está seguro de que su entidad no ha sido modificada (o si acepta que se perderá alguna modificación), puede volver a adjuntarla a la sesión con bloqueo.
No bloqueará nada, pero obtendrá la entidad del caché de la sesión o (si no se encuentra allí) la leerá desde la base de datos.
Es muy útil para evitar LazyInitException cuando se navega por relaciones de entidades "antiguas" (de la HttpSession, por ejemplo). Primero "vuelve a adjuntar" la entidad.
El uso de get también puede funcionar, excepto cuando obtiene la herencia asignada (que ya arrojará una excepción en getId ()).
fuente
Session.lock(entity, LockMode.NONE)
falla con la excepción que dice: no se pudo reasociar la colección transitoria no inicializada. ¿Cómo puede superar esto?Session.find()
método API. Quizás te refieresSession.load(Object object, Serializable id)
.Estados de la entidad
JPA define los siguientes estados de entidad:
Nuevo (transitorio)
Se considera que un objeto recién creado que nunca se ha asociado con un Hibernate
Session
(también conocido comoPersistence Context
) y que no está asignado a ninguna fila de la tabla de la base de datos está en el estado Nuevo (Transitorio).Para ser persistente, necesitamos llamar explícitamente el
EntityManager#persist
método o hacer uso del mecanismo de persistencia transitiva.Persistente (Gestionado)
Una entidad persistente se ha asociado con una fila de la tabla de la base de datos y está siendo administrada por el Contexto de persistencia actualmente en ejecución. Cualquier cambio realizado en dicha entidad se detectará y propagará a la base de datos (durante el tiempo de descarga de la sesión).
Con Hibernate, ya no tenemos que ejecutar instrucciones INSERT / UPDATE / DELETE. Hibernate emplea un estilo de trabajo transaccional de reescritura y los cambios se sincronizan en el último momento responsable, durante el
Session
tiempo de descarga actual .Separado
Una vez que el contexto de persistencia actualmente en ejecución se cierra, todas las entidades administradas previamente se separan. Los cambios sucesivos ya no se rastrearán y no se realizará una sincronización automática de la base de datos.
Transiciones de estado de entidad
Puede cambiar el estado de la entidad utilizando varios métodos definidos por la
EntityManager
interfaz.Para comprender mejor las transiciones de estado de la entidad JPA, considere el siguiente diagrama:
Al usar JPA, para volver a asociar una entidad separada a una activa
EntityManager
, puede usar la operación de fusión .Al usar la API nativa de Hibernate, además de
merge
, puede volver a conectar una entidad separada a una sesión de Hibernate activa utilizando los métodos de actualización, como se demuestra en el siguiente diagrama:Fusionar una entidad separada
La fusión va a copiar el estado de la entidad separada (fuente) en una instancia de entidad administrada (destino).
Considere que hemos persistido en la siguiente
Book
entidad, y ahora la entidad está separada yaEntityManager
que la que se usó para persistir la entidad se cerró:Mientras la entidad está en estado separado, la modificamos de la siguiente manera:
Ahora, queremos propagar los cambios a la base de datos, para que podamos llamar al
merge
método:E Hibernate ejecutará las siguientes instrucciones SQL:
Si la entidad fusionada no tiene equivalente en la actual
EntityManager
, se obtendrá una instantánea de entidad nueva de la base de datos.Una vez que hay una entidad administrada, JPA copia el estado de la entidad separada en la que está administrada actualmente, y durante el Contexto de persistencia
flush
, se generará una ACTUALIZACIÓN si el mecanismo de verificación sucio encuentra que la entidad administrada ha cambiado.Volver a unir una entidad separada
Hibernate, pero no JPA admite la reconexión a través del
update
método.Un Hibernate
Session
solo puede asociar un objeto de entidad para una fila de base de datos dada. Esto se debe a que el contexto de persistencia actúa como un caché en memoria (caché de primer nivel) y solo un valor (entidad) está asociado con una clave dada (tipo de entidad e identificador de base de datos).Una entidad se puede volver a unir solo si no hay otro objeto JVM (que coincida con la misma fila de la base de datos) ya asociado con la Hibernación actual
Session
.Teniendo en cuenta que hemos persistido en la
Book
entidad y que la hemos modificado cuando laBook
entidad estaba en estado separado:Podemos volver a conectar la entidad separada de esta manera:
E Hibernate ejecutará la siguiente instrucción SQL:
A diferencia
merge
, la entidad separada proporcionada se volverá a asociar con el contexto de persistencia actual y se programa una ACTUALIZACIÓN durante el vaciado, ya sea que la entidad se haya modificado o no.Para evitar esto, puede usar la
@SelectBeforeUpdate
anotación Hibernate que activará una instrucción SELECT que obtuvo el estado cargado que luego utiliza el mecanismo de verificación sucio.Cuidado con la excepción de objeto único
Un problema con el que puede ocurrir
update
es si el contexto de persistencia ya contiene una referencia de entidad con la misma identificación y del mismo tipo que en el siguiente ejemplo:Ahora, al ejecutar el caso de prueba anterior, Hibernate arrojará un
NonUniqueObjectException
porque el segundoEntityManager
ya contiene unaBook
entidad con el mismo identificador al que pasamosupdate
, y el Contexto de persistencia no puede contener dos representaciones de la misma entidad.Conclusión
Es
merge
preferible el método si está utilizando un bloqueo optimista, ya que le permite evitar actualizaciones perdidas. Para obtener más detalles sobre este tema, consulte este artículo .El
update
es bueno para actualizaciones por lotes, ya que puede evitar que la instrucción SELECT adicional generado por lamerge
operación, por lo tanto, reduciendo el tiempo de ejecución por lotes de actualización.fuente
@SelectBeforeUpdate
embargo, me preguntaba acerca de la anotación. ¿Cuándo se activa la selección? Al llamarupdate
, justo antes de enjuagar o realmente no importa (¿podría importar si hibernate recupera todas las entidades anotadas en una llamada antes de enjuagar)?@SelectBeforeUpdate
activa la SELECT durante el contexto de persistenciaflush
operación. Consulte elgetDatabaseSnapshot
método enDefaultFlushEntityEventListener
para más detalles.Regresé a JavaDoc
org.hibernate.Session
y encontré lo siguiente:Por lo tanto
update()
,saveOrUpdate()
,lock()
,replicate()
ymerge()
son las opciones propuestas.update()
: Lanzará una excepción si hay una instancia persistente con el mismo identificador.saveOrUpdate()
: Guardar o actualizarlock()
: Obsoletoreplicate()
: Persiste el estado de la instancia separada dada, reutilizando el valor del identificador actual.merge()
: Devuelve un objeto persistente con el mismo identificador. La instancia dada no se asocia con la sesión.Por lo tanto,
lock()
no debe usarse de inmediato y, en función del requisito funcional, se puede elegir uno o más de ellos.fuente
Lo hice de esa manera en C # con NHibernate, pero debería funcionar de la misma manera en Java:
Se llamó a First Lock en cada objeto porque Contains siempre fue falso. El problema es que NHibernate compara objetos por identificación y tipo de base de datos. Contiene utiliza el
equals
método, que se compara por referencia si no se sobrescribe. Con eseequals
método funciona sin excepciones:fuente
Session.contains(Object obj)
comprueba la referencia y no detectará una instancia diferente que represente la misma fila y que ya esté asociada a ella.Aquí mi solución genérica para Entidades con una propiedad de identificador.
Este es uno de los pocos aspectos de .Net EntityFramework que me gustan, las diferentes opciones de conexión con respecto a las entidades modificadas y sus propiedades.
fuente
Se me ocurrió una solución para "actualizar" un objeto del almacén de persistencia que tendrá en cuenta otros objetos que ya pueden estar conectados a la sesión:
fuente
Lo sentimos, parece que no puedo agregar comentarios (¿todavía?).
Usando Hibernate 3.5.0-Final
Mientras que el
Session#lock
método de esta obsoleto, el Javadoc no sugieren el usoSession#buildLockRequest(LockOptions)#lock(entity)
y si usted asegurarse de que sus asociaciones tienencascade=lock
, los perezosos de carga no es un problema tampoco.Entonces, mi método de conexión se parece un poco
Las pruebas iniciales sugieren que funciona de maravilla.
fuente
Quizás se comporta ligeramente diferente en Eclipselink. Para volver a adjuntar objetos separados sin obtener datos obsoletos, generalmente hago:
y como opcional un segundo paso (para invalidar las cachés):
fuente
intente getHibernateTemplate (). replicate (entidad, ReplicationMode.LATEST_VERSION)
fuente
En la publicación original, hay dos métodos,
update(obj)
ymerge(obj)
se menciona que funcionan, pero en circunstancias opuestas. Si esto es realmente cierto, entonces, ¿por qué no probar para ver si el objeto ya está en la sesión primero y luego llamarupdate(obj)
si es así? De lo contrario, llamemerge(obj)
.La prueba de existencia en la sesión es
session.contains(obj)
. Por lo tanto, creo que el siguiente pseudocódigo funcionaría:fuente
para volver a conectar este objeto, debe usar merge ();
este método acepta en el parámetro que su entidad se separó y devuelve una entidad que se adjuntará y volverá a cargar desde la base de datos.
fuente
llamar primero merge () (para actualizar la instancia persistente), luego bloquear (LockMode.NONE) (para adjuntar la instancia actual, no la que devuelve merge ()) parece funcionar para algunos casos de uso.
fuente
La propiedad
hibernate.allow_refresh_detached_entity
hizo el truco para mí. Pero es una regla general, por lo que no es muy adecuado si desea hacerlo solo en algunos casos. Espero que ayude.Probado en Hibernate 5.4.9
SessionFactoryOptionsBuilder
fuente
El soporte de Hibernate vuelve a conectar la entidad separada de forma serval, consulte la guía del usuario de Hibernate .
fuente
fuente