transaction transaccional example driven annotation java hibernate spring transactions

java - transaccional - Intento ilegal de asociar una colección con dos sesiones abiertas



spring transaction propagation (7)

El problema fue con el mapeo de filtro de vista abierta en sesión. Estaba creando una sesión en getSession y otra en guardar.

Puede cambiar solo singleSession como falso predeterminado es verdadero

Estoy tratando de agregar un pojo a una colección en otro pojo. Estoy seguro de que estoy cometiendo un error muy estúpido en algún punto del camino, pero no puedo encontrar la manera de resolverlo.

Tengo un pojo LookupTable que contiene una lista de columnas:

public class LookupTable { private long id; // More properties go here... private List<Column> columns; public void addColumn(Column column) { this.columns.add(column); } // More methods go here... }

En mi configuración de hibernación tengo:

<class name="LookupTable" table="ARR_LOOKUP_TABLE"> <id name="id" column="ID"> <generator class="native"/> </id> <!-- Some properties here --> <bag name="columns" cascade="all,delete-orphan" access="field"> <key column="LOOKUP_TABLE" not-null="true"/> <one-to-many class="Column"/> </bag> </class> <class name="Column" table="ARR_LOOKUP_COLUMN"> <id name="id" column="ID"> <generator class="native"/> </id> <!-- Some properties here --> </class>

En mi archivo de configuración Spring tengo:

<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="managers" expression="execution(public * com.foo.*Manager.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="managers"/> </aop:config>

Y finalmente el código donde falla todo dentro de mi clase de administrador (com.foo.LookupTableManager):

public void addColumnToTable(Column column, long tableId) { LookupTable lookupTable = this.lookupTableDao.findById(tableId); lookupTable.addColumn(column); this.lookupTableDao.saveOrUpdate(lookupTable); }

La variable lookupTableDao aquí se refiere a una clase DAO simple que extiende HibernateDaoSupport.

El error que recibo es:

org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions at org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:410) at org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:43) at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101) at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61) at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55) at org.hibernate.event.def.AbstractVisitor.process(AbstractVisitor.java:123) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:293) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70) at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507) at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499) at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495) at com.foo.AbstractDao.saveOrUpdate(AbstractDao.java:29) at com.foo.LookupTableManager.addColumnToTable(LookupTableManager.java:338) ... etc ...

OK, entiendo el mensaje básico que estoy recibiendo. Pero lo que no entiendo es de dónde saco la segunda sesión ... ¿Alguien puede ayudarme con esto?

Estoy usando Hibernate 3.2.6.ga, Spring 2.5.5 y Tomcat 6.0


Oh mierda, la solución fue un poco demasiado simple. Resulta que no tuve una transacción en absoluto. Usé casi la misma configuración de transacción en uno de mis otros archivos de configuración. El punto de corte de allí también se llamaba "gerentes", por lo que mi asesor aquí estaba haciendo referencia al punto de corte en el otro archivo. Renombrar el pointcut resolvió mi problema.


Supongo que la llamada lookupTableDao.findById está obteniendo su objeto en una sesión, pero el lookupTableDao.saveOrUpdate es diferente, ¿cómo se obtiene el objeto Session través de Spring?

¿De dónde viene el objeto Column ? ¿Ya está en la base de datos o es nuevo?


Al parecer, el uso de merge probablemente solucionará el problema

myInstance = (myInstanceClass) Session.merge(myInstance);


Si solo quiere una transacción sobre una parte de su código, puede usar algo como esto:

import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction;

y en la clase:

@Autowired private SessionFactory sessionFactory;

Más tarde, alrededor del código que debería hacer cosas en la misma transacción:

Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Stuff stuff = getStuff(); manipulate(stuff); send(stuff); save(stuff); tx.commit();

Utilizo esto dentro de un bucle en algunos trabajos por lotes de larga ejecución.


Tuve el mismo problema con mi siguiente código: quería actualizar una tienda de cierta entidad. Lo estaba recuperando de este método:

@Override public Store getStoreByStoreId(final long storeId) { getHibernateTemplate().execute(new HibernateCallback<Store>() { @Override public StoredoInHibernate(Session session) throws HibernateException, SQLException { return (Store) session.createCriteria(Store.class) .add(Restrictions.eq(Store.PROP_ID, storeId)) .uniqueResult(); } }); }

Luego, estaba actualizando la tienda en el siguiente método:

@Override public void updateStoreByStoreId(final long storeId) { getHibernateTemplate().execute(new HibernateCallback<Void>() { @Override public Void doInHibernate(Session session) throws HibernateException, SQLException { Store toBeUpdated = getStoreByStoreId(storeId); if (toBeUpdated != null){ // ..change values for certain fields session.update(toBeUpdated); } return null; } }); }

Resultó que recibí el error "Intento ilegal ..." porque la primera sesión del método get no se estaba cerrando, y estaba tratando de actualizar con la otra sesión de la tienda. La solución para mí fue mover la llamada para recuperar la tienda a actualizar en el método de actualización.

@Override public void updateStoreByStoreId(final long storeId) { getHibernateTemplate().execute(new HibernateCallback<Void>() { @Override public Void doInHibernate(Session session) throws HibernateException, SQLException { Store toBeUpdated = (Store) session.createCriteria(Store.class) .add(Restrictions.eq(Store.PROP_ID, storeId)) .uniqueResult(); if (toBeUpdated != null){ // ..change values for certain fields session.update(toBeUpdated ); } return null; } }); }


Estaba usando Windows Server IIS y simplemente cambiando el modo AppPool Managed Pipeline de Integrated to Classic resolvió mi problema.

Gracias