Незаконная попытка связать коллекцию с двумя открытыми сеансами
Я пытаюсь добавить pojo в коллекцию в другое pojo. Я уверен, что я делаю по-настоящему глупую ошибку где-то рядом, но я не могу понять, как ее решить.
У меня есть pojo LookupTable, который содержит список столбцов:
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...
}
В моей конфигурации спящего режима у меня есть:
<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>
В моем конфигурационном файле Spring у меня есть:
<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>
И, наконец, код, где все это не удается в моем классе менеджера (com.foo.LookupTableManager):
public void addColumnToTable(Column column, long tableId) {
LookupTable lookupTable = this.lookupTableDao.findById(tableId);
lookupTable.addColumn(column);
this.lookupTableDao.saveOrUpdate(lookupTable);
}
Переменная lookupTableDao здесь относится к простому классу DAO, который расширяет HibernateDaoSupport.
Ошибка, которую я получаю:
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 ...
ОК, я понимаю основное сообщение, которое я получаю. Но я не понимаю, где я получу вторую сессию. Может ли кто-нибудь помочь мне с этим?
Я использую Hibernate 3.2.6.ga, Spring 2.5.5 и Tomcat 6.0
Ответы
Ответ 1
О, дерьмо, решение было слишком простым.
Оказывается, у меня совсем не было транзакции. Я использовал почти ту же конфигурацию транзакций в одном из моих других файлов конфигурации. Точечный фрагмент там также назывался "менеджерами", поэтому мой советник здесь ссылался на pointcut в другом файле.
Переименование pointcut решило мою проблему.
Ответ 2
Я предполагаю, что вызов lookupTableDao.findById
получает ваш объект за один сеанс, но lookupTableDao.saveOrUpdate
отличается от него - как вы получаете объект Session
через Spring?
Где находится объект Column
- это уже в БД или новом?
Ответ 3
Проблема заключалась в сопоставлении фильтра Open Session In view.
Это было создание сеанса на getSession
И другие при сохранении.
Вы можете изменить только singleSession как false
default его true
Ответ 4
Очевидно, использование слияния, вероятно, устранит проблему
myInstance = (myInstanceClass) Session.merge(myInstance);
Ответ 5
У меня была такая же проблема с моим следующим кодом:
Я хотел обновить определенный магазин. Я извлек его из этого метода:
@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();
}
});
}
Затем я обновлял хранилище следующим способом:
@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;
}
});
}
Оказалось, что я получил ошибку "Незаконная попытка...", потому что первый сеанс из метода get не закрывался, и я пытался обновить с другим сеансом хранилище. Решение для меня состояло в том, чтобы переместить вызов для извлечения хранилища для обновления в методе обновления.
@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;
}
});
}
Ответ 6
Если вы хотите только транзакцию над частью своего кода, вы можете использовать что-то вроде этого:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
и в классе:
@Autowired private SessionFactory sessionFactory;
Позже, вокруг кода, который должен делать вещи в одной транзакции:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Stuff stuff = getStuff();
manipulate(stuff);
send(stuff);
save(stuff);
tx.commit();
Я использую это внутри цикла в некоторых длительных пакетных заданиях.
Ответ 7
Я использовал сервер IIS для Windows и просто изменил режим управляемого приложения AppPool от Integrated to Classic, решив мою проблему.
Спасибо