Question

I'm trying to add a pojo to a collection in another pojo. I'm sure I'm making a really stupid mistake somewhere along the lines but I can't figure out how to solve it.

I have a pojo LookupTable which contains a list of Columns:

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...
}

In my hibernate configuration I have:

<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>

In my Spring config file I have:

<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>

And finally the code where it all fails within my manager class (com.foo.LookupTableManager):

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

The variable lookupTableDao here refers to a simple DAO class which extends HibernateDaoSupport.

The error I get is:

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, I understand the basic message I'm getting. But what I don't understand is where I get the second session.... Can anyone help me with this?

I'm using Hibernate 3.2.6.ga, Spring 2.5.5 and Tomcat 6.0

Was it helpful?

Solution

Turns out I didn't have a transaction at all. I used almost the same transaction configuration in one of my other config files.

The pointcut over there was also called "managers", so my advisor here was referencing the pointcut in the other file.

Renaming the pointcut solved my problem.

OTHER TIPS

My guess is that the lookupTableDao.findById call is getting your object in one session, but the lookupTableDao.saveOrUpdate is a different one - how do you get the Session object, via Spring?

Where is the Column object coming from - is that already on the DB or new?

The problem was with the Open Session In view filter mapping. It was creating a session on getSession And other on save.

You can change only singleSession as false default its true

Apparently using merge will probably fix the problem

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

I had the same problem with my following code: I wanted to update a certain entity Store. I was retrieving it from this method:

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

Then, I was updating the store in the following method:

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

It turns out I got the "Illegal attempt ..." error because the first session from the get method was not closing, and I was trying to update with the other session the store. The solution for me was to move the call for retrieving the store to be updated in the update method.

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

If you only want a transaction over a part of your code you can use something like this:

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

and in the class:

@Autowired private SessionFactory sessionFactory;

Later, around the code that should do things in the same transaction:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

Stuff stuff = getStuff();
manipulate(stuff);
send(stuff);
save(stuff);

tx.commit();

I use this inside a loop in some long-running batch jobs.

I was using windows server IIS and just changing the AppPool Managed Pipeline mode from Integrated to Classic solved my issue.

Thanks

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top