Незаконная попытка связать коллекцию с двумя открытыми сеансами

StackOverflow https://stackoverflow.com/questions/221079

Вопрос

Я пытаюсь добавить pojo в коллекцию в другом pojo.Я уверен, что где-то совершаю действительно глупую ошибку, но не могу понять, как ее решить.

У меня есть таблица поиска pojo, содержащая список столбцов:

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.

Это было полезно?

Решение

Оказывается, у меня вообще не было транзакции. Я использовал почти ту же конфигурацию транзакции в одном из моих других файлов конфигурации.

Точка отсчета там также называлась «менеджеры», поэтому мой советник ссылался на точку в другом файле.

Переименование pointcut решило мою проблему.

Другие советы

Я предполагаю, что вызов lookupTableDao.findById получает ваш объект за один сеанс, но lookupTableDao.saveOrUpdate другой - как вы получаете < Объект code> Session , через Spring?

Откуда берется объект Column - он уже находится в БД или новый?

Проблема была в отображении фильтра представления Open Session In. Это было создание сессии на getSession И другие на сохранение.

Вы можете изменить только singleSession как ложный по умолчанию это правда

Видимо, слияние, вероятно, решит проблему

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

У меня была та же проблема с моим следующим кодом: Я хотел обновить определенную сущность Store. Я извлекал его из этого метода:

@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;
        }
    });
} 

Если вам нужна транзакция только над частью вашего кода, вы можете использовать что-то вроде этого:

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();

Я использую это внутри цикла в некоторых длительных пакетных заданиях.

Я использовал Windows Server IIS, и просто смена режима управляемого конвейера AppPool с интегрированного на классический решила мою проблему.

Спасибо

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top