解决 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来的,  在此留个记号,需要做进一步的学习。

 

 

 

 

### 错误分析与解决方案 在 Verilog 或 SystemVerilog 中,将句柄与打包类型进行比较时出现错误,通常是因为这两种数据类型的底层结构或存储方式不同。句柄(handle)通常用于指向类实例或对象,而打包类型(packed type)则是固定宽度的位向量[^3]。 #### 1. 数据类型不匹配问题 句柄和打包类型属于不同的数据类别。句柄指向的是对象实例,而打包类型表示的是硬件寄存器或信号的位宽。直接比较两者会导致编译器无法解析其语义,从而引发错误。例如: ```systemverilog class MyClass; bit [7:0] data; endclass MyClass obj = new(); bit [7:0] packed_data; if (obj == packed_data) begin // Error: Cannot compare class handle with packed type // ... end ``` 此代码会抛出错误,因为 `obj` 是一个类句柄,而 `packed_data` 是一个打包的位向量。 #### 2. 解决方法 为了正确比较,需要从句柄中提取可比较的数据,并将其转换为打包类型。以下是一个示例: ```systemverilog if (obj.data == packed_data) begin // Correct: Compare the packed data inside the object // ... end ``` 在此示例中,`obj.data` 是从类实例中提取的打包数据,可以直接与另一个打包类型变量 `packed_data` 进行比较。 #### 3. 类型转换注意事项 如果需要更复杂的类型转换,可以使用强制类型转换(casting)。例如: ```systemverilog bit [7:0] extracted_data = obj.data; // Extract packed data from handle if (extracted_data == packed_data) begin // Correct: Explicit extraction and comparison // ... end ``` 此外,确保句柄所指向的对象已经正确初始化,否则可能会导致运行时错误。 #### 4. 动态规划与性能优化 虽然动态规划方法通常能够提供更优的解决方案[^2],但在硬件描述语言中,算法的选择应根据具体应用场景调整。例如,在硬件设计中,贪婪算法可能更适合实时性要求较高的场景,而动态规划可能更适合资源受限但对结果精确度要求较高的场景。 ### 示例代码 以下是一个完整的代码示例,展示如何正确比较句柄与打包类型: ```systemverilog class DataPacket; bit [7:0] payload; endclass DataPacket packet = new(); bit [7:0] received_payload; initial begin packet.payload = 8'h5A; received_payload = 8'h5A; if (packet.payload == received_payload) begin // Correct: Compare packed data $display("Payload matches!"); end else begin $display("Payload mismatch!"); end end ``` #### 5. 性能优化建议 对于每操作需要 6 个时钟周期的解决方案[^1],可以通过减少不必要的类型转换和优化数据路径来进一步提升性能。例如,避免多次访问类成员变量,或者将频繁使用的值缓存到局部变量中。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值