解决 Illegal attempt to associate a collection with two open sessions

今天碰到了一个 奇怪的问题 Illegal attempt to associate a collection with two open sessions。  在最后提交逻辑里。  以前也说过, 现在这个项目的事务是利用AOP 加到DAO 层上的, 在EJB 的方法中很容易在一个方法里面多次调用DAO 层。 并且由于是在做mass change 之前要取出原业务对象 然后在merge 上mass change 的数据。  然后在做N 多的校验, 到最后校验通过提交数据。  

这个异常的 log 如下:

org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions

at org.hibernate.exception.NestableRuntimeException.<init>(NestableRuntimeException.java:104) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:403) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:43) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.AbstractVisitor.process(AbstractVisitor.java:123) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:293) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495) ~[hibernate-3.2.6.ga.jar:3.2.6.ga]

进入 OnUpdateVisitor.java的第 43 行代码 可以看到

PersistentCollection wrapper = (PersistentCollection) collection;

if ( wrapper.setCurrentSession(session) ) {

  的确在我们的DAO 中要保存的对象有一个 List 成员 所以 这个 wrapper 就是Hibernate 在 load 出那个 entity 的时候产生出的对应的PersistentCollection 集合了, 它应该还保留当时的那个DAO 用到的。  我们再到AbstractPersistentCollection.setCurrentSession 方法里看看:

public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
		if (session==this.session) {
			return false;
		}
		else {
			if ( isConnectedToSession() ) {
				CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
				if (ce==null) {
					throw new HibernateException(
							"Illegal attempt to associate a collection with two open sessions"
						);
				}
				else {
					throw new HibernateException(
							"Illegal attempt to associate a collection with two open sessions: " +
							MessageHelper.collectionInfoString( 
									ce.getLoadedPersister(), 
									ce.getLoadedKey(), 
									session.getFactory() 
								)
						);
				}
			}
			else {
				this.session = session;
				return true;
			}
		}
	}
 

   在这个方法里面程序的行为应该是:

    1,  isConnectedToSession()  应该返回为true, 当然这个Session 不是当前这个Session,而是开始load 的那个DAO  对应的Session,

    2,接下来一行 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);就返回 null 了,

    3 ,if (ce==null) {
throw new HibernateException(
"Illegal attempt to associate a collection with two open sessions"
);
       } 

    解决的方法 有两个:

      1,  首先 Object  mergedEntity = Session.merge(entity), 然后 saveOrUpdate 这个 mergedEntity。

       2,  Session.lock (entity,  Lock.NONE)。

    其实这个 lock 的用法还是从 这个地方google来的,  在此留个记号,需要做进一步的学习。

猜你喜欢

转载自bruce008.iteye.com/blog/1388930