manager java hibernate spring session detach

java - manager - ¿Cuál es la forma correcta de volver a colocar los objetos desprendidos en Hibernate?



detach hibernate (15)

Tengo una situación en la que necesito volver a adjuntar objetos separados a una sesión de hibernación, aunque un objeto con la misma identidad PUEDE existir en la sesión, lo que provocará errores.

En este momento, puedo hacer una de dos cosas.

  1. getHibernateTemplate().update( obj ) Esto funciona si y solo si un objeto no existe en la sesión de hibernación. Se lanzan excepciones que indican que un objeto con el identificador dado ya existe en la sesión cuando lo necesito más tarde.

  2. getHibernateTemplate().merge( obj ) Esto funciona si y solo si un objeto existe en la sesión de hibernación. Se lanzan excepciones cuando necesito que el objeto esté en una sesión más tarde si lo uso.

Dados estos dos escenarios, ¿cómo puedo adjuntar genéricamente sesiones a 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 ...


En la publicación original, hay dos métodos, update(obj) y merge(obj) que se mencionan para trabajar, 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 llamar a la update(obj) si es así, de lo contrario llamar a merge(obj) .

La prueba de existencia en la sesión es session.contains(obj) . Por lo tanto, creo que el siguiente pseudocódigo funcionaría:

if (session.contains(obj)) { session.update(obj); } else { session.merge(obj); }


Entonces parece que no hay forma de volver a conectar una entidad obsoleta en JPA.

merge() enviará el estado obsoleto a la base de datos y sobrescribirá las actualizaciones intermedias.

No se puede llamar a refresh() en una entidad separada.

no se puede llamar a lock() en una entidad separada, e incluso si pudiera, y volviera a conectar la entidad, llamar a ''lock'' con el argumento ''LockMode.NONE'' implica que estás bloqueando, pero no bloqueando, es la pieza más contraintuitiva del diseño de API que he visto.

Entonces estás estancado. Hay un método detach() , pero no attach() o reattach() . Un paso obvio en el ciclo de vida del objeto no está disponible para usted.

A juzgar por el número de preguntas similares sobre JPA, parece que incluso si JPA afirma tener un modelo coherente, ciertamente no coincide con el modelo mental de la mayoría de los programadores, quienes han sido maldecidos por perder muchas horas tratando de entender cómo obtener JPA para hacer las cosas más simples, y terminar con el código de administración de caché en todas sus aplicaciones.

Parece que la única forma de hacerlo es descartar su entidad desactualizada y hacer una consulta de búsqueda con la misma ID, que golpeará la L2 o la base de datos.

Mik


Lo hice de esa manera en C # con NHibernate, pero debería funcionar de la misma manera en Java:

public virtual void Attach() { if (!HibernateSessionManager.Instance.GetSession().Contains(this)) { ISession session = HibernateSessionManager.Instance.GetSession(); using (ITransaction t = session.BeginTransaction()) { session.Lock(this, NHibernate.LockMode.None); t.Commit(); } } }

Se llamó a First Lock en cada objeto porque Contiene siempre fue falso. El problema es que NHibernate compara objetos por ID y tipo de base de datos. Contiene utiliza el método equals , que se compara por referencia si no se sobrescribe. Con ese método equals , funciona sin ninguna excepción:

public override bool Equals(object obj) { if (this == obj) { return true; } if (GetType() != obj.GetType()) { return false; } if (Id != ((BaseObject)obj).Id) { return false; } return true; }


Lo sentimos, parece que no puedo agregar comentarios (¿todavía?).

Usando Hibernate 3.5.0-Final

Mientras que el método de Session#lock esta en desuso, el javadoc sugiere usar Session#buildLockRequest(LockOptions)#lock(entity) y si se asegura de que sus asociaciones tengan cascade=lock , la carga diferida tampoco es un problema.

Entonces, mi método adjunto se parece un poco

MyEntity attach(MyEntity entity) { if(getSession().contains(entity)) return entity; getSession().buildLockRequest(LockOptions.NONE).lock(entity); return entity;

Las pruebas iniciales sugieren que funciona como un regalo.


Prueba getHibernateTemplate (). replicate (entity, ReplicationMode.LATEST_VERSION)


Quizás se comporta ligeramente diferente en Eclipselink. Para volver a adjuntar objetos separados sin obtener datos obsoletos, normalmente hago:

Object obj = em.find(obj.getClass(), id);

y como un segundo paso opcional (para obtener cachés invalidados):

em.refresh(obj)


Se me ocurrió una solución para "actualizar" un objeto del almacén de persistencia que contabilizará otros objetos que ya pueden estar adjuntos a la sesión:

public void refreshDetached(T entity, Long id) { // Check for any OTHER instances already attached to the session since // refresh will not work if there are any. T attached = (T) session.load(getPersistentClass(), id); if (attached != entity) { session.evict(attached); session.lock(entity, LockMode.NONE); } session.refresh(entity); }


Si está seguro de que su entidad no ha sido modificada (o si acepta que se perderá alguna modificación), puede volver a conectarla a la sesión con bloqueo.

session.lock(entity, LockMode.NONE);

No bloqueará nada, pero obtendrá la entidad de la memoria caché de la sesión o (si no se encuentra allí) la leerá desde la base de datos.

Es muy útil evitar LazyInitException cuando se navega por relaciones desde una entidad "antigua" (de la HttpSession por ejemplo). Primero "vuelve a conectar" la entidad.

El uso de get también puede funcionar, excepto cuando obtiene un mapeo de herencia (que ya arrojará una excepción en el getId ()).

entity = session.get(entity.getClass(), entity.getId());


Todas 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 pasa son los que se hacen gestionados.

merge () en realidad no es una API de (re) conexión. Observe que merge () tiene un valor de retorno? Eso es porque 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 ingresa para fusionar () ya está administrado (ya asociado con la Sesión), entonces ese es el gráfico con el que Hibernate trabaja; el objeto pasado es el mismo objeto devuelto por merge (). Sin embargo, si el objeto que pasa a 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 la haga funcionar", depende de si también desea contabilizar datos que aún no se han insertado. Suponiendo que lo haga, use algo como

if ( session.contains( myEntity ) ) { // nothing to do... myEntity is already associated with the session } else { session.saveOrUpdate( myEntity ); }

Tenga en cuenta que utilicé saveOrUpdate () en lugar de update (). Si no desea que los datos no insertados aún se manejen aquí, use update () en su lugar ...


Volví al org.hibernate.Session para org.hibernate.Session y encontré lo siguiente:

Las instancias transitorias pueden hacerse persistentes llamando a save() , saveOrUpdate() o saveOrUpdate() . Las instancias persistentes se pueden convertir en transitorias llamando a delete() . Cualquier instancia devuelta por un método get() o load() es persistente. Las instancias separadas pueden hacerse persistentes llamando a update() , saveOrUpdate() , lock() o replicate() . El estado de una instancia transitoria o separada también puede hacerse persistente como una nueva instancia persistente llamando a merge() .

Por lo tanto, update() , saveOrUpdate() , lock() , replicate() y merge() son las opciones candidatas.

update() : Lanzará una excepción si hay una instancia persistente con el mismo identificador.

saveOrUpdate() : guardar o actualizar

lock() : obsoleto

replicate() : persista 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 forma inmediata y, en función del requisito funcional, se pueden elegir uno o más de ellos.


llamar a first merge () (para actualizar la instancia persistente), luego bloquear (LockMode.NONE) (para adjuntar la instancia actual, no la devuelta por merge ()) parece funcionar para algunos casos de uso.


para volver a conectar este objeto, debe usar merge ();

este método acepta en el parámetro su entidad separada y devuelve una entidad que se adjuntará y volverá a cargar desde la base de datos.

Example : Lot objAttach = em.merge(oldObjDetached); objAttach.setEtat(...); em.persist(objAttach);


Session.contains(Object obj) comprueba la referencia y no detectará una instancia diferente que represente la misma fila y que ya esté adjuntada.

Aquí mi solución genérica para entidades con una propiedad de identificador.

public static void update(final Session session, final Object entity) { // if the given instance is in session, nothing to do if (session.contains(entity)) return; // check if there is already a different attached instance representing the same row final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass()); final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session); final Object sessionEntity = session.load(entity.getClass(), identifier); // override changes, last call to update wins if (sessionEntity != null) session.evict(sessionEntity); session.update(entity); }

Este es uno de los pocos aspectos de .Net EntityFramework que me gusta, las diferentes opciones de adjuntar a las entidades modificadas y sus propiedades.


Respuesta no diplomática: probablemente esté buscando un contexto de persistencia extendido. Esta es una de las principales razones detrás de seamframework.org ... Si está luchando por usar Hibernate en Spring en particular, eche un vistazo a esta parte de los documentos de Seam.

Respuesta diplomática: esto se describe en los documentos de Hibernate . Si necesita más aclaración, eche un vistazo a la Sección 9.3.2 de Java Persistence con Hibernate llamada "Trabajar con objetos separados". Recomiendo encarecidamente que obtengas este libro si estás haciendo algo más que CRUD con Hibernate.


try getHibernateTemplate().saveOrUpdate()