Hibernate异常:found two representations of same collection

bojianyu 2008-05-04 02:39:56
请大家帮忙,给出解决方案,以前有很多人问过,好像都没有好的解决方法。在线等,问题解决立即结贴!
...全文
3120 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
blacksky115 2009-06-16
  • 打赏
  • 举报
回复
相关问题及解决办法:
FYI, if you perform the update() of the object in a subsequent session, the update will work correctly. It only errs out when you do the update() in the same session in which you have performed a clear() operation.

I've looked through the SessionImpl, but I'm not yet familiar with the code base and can't figure out why the error is being issued. But I strongly feel that this should not be an error condition.
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping package="com.everstream.idg.data">

<class name="Load" table="LOAD">
<id name="id" column="LOAD_ID" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_LOAD_ID</param>
</generator>
</id>
<property name="startTime" column="LOAD_START_TIME" not-null="true"/>
<property name="endTime" column="LOAD_END_TIME" not-null="false"/>
<property name="type" column="LOAD_TYPE" not-null="true"/>
<property name="status" column="LOAD_STATUS" not-null="true"/>
<property name="recordsRead" column="RECORDS_READ" not-null="false"/>
<property name="recordsWritten" column="RECORDS_WRITTEN" not-null="false"/>
<property name="recordsWithErrors" column="RECORDS_WITH_ERRORS" not-null="false"/>
<property name="recordsDiscarded" column="RECORDS_DISCARDED" not-null="false"/>
<list name="messages" table="LOAD_MSG" lazy="true" cascade="all">
<key column="LOAD_ID"/>
<index column="LOAD_MSG_SEQ_NBR"/>
<composite-element class="LoadMessage">
<property name="timestamp" column="LOAD_MSG_TIME" not-null="true"/>
<property name="message" column="LOAD_MSG_TXT" not-null="false"/>
</composite-element>
</list>
<many-to-one name="source" class="Source" column="SOURCE_ID" />
</class>

</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Code:
Load load = new Load();
load.setStartTime(new Timestamp(System.currentTimeMillis()));
load.setType("xxx");
load.setStatus("xxx");
load.addMessage("xxx");
session.save.save(load);
session.flush();
session.clear();
load.addMessage("yyy");
session.update(load);
session.flush();


Full stack trace of any exception that occurs:
Code:
com.everstream.s4.database.TransactionException: net.sf.hibernate.HibernateException: Found two representations of same collection: com.everstream.idg.data.Load.messages
at com.everstream.s4.database.Transaction.completeTransaction(Transaction.java:345)
at com.everstream.s4.database.Transaction.commit(Transaction.java:110)
at com.everstream.idg.common.Main.test(Main.java:86)
at com.everstream.idg.common.Main.main(Main.java:39)
Caused by: net.sf.hibernate.HibernateException: Found two representations of same collection: com.everstream.idg.data.Load.messages
at net.sf.hibernate.impl.SessionImpl.updateReachableCollection(SessionImpl.java:2870)
at net.sf.hibernate.impl.FlushVisitor.processCollection(FlushVisitor.java:32)
at net.sf.hibernate.impl.AbstractVisitor.processValue(AbstractVisitor.java:69)
at net.sf.hibernate.impl.AbstractVisitor.processValues(AbstractVisitor.java:36)
at net.sf.hibernate.impl.SessionImpl.flushEntity(SessionImpl.java:2592)
at net.sf.hibernate.impl.SessionImpl.flushEntities(SessionImpl.java:2458)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2260)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2239)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at com.everstream.s4.database.Transaction$SessionResource.commit(Transaction.java:367)
at com.everstream.s4.database.Transaction.completeTransaction(Transaction.java:304)
... 3 more
Exception in thread "main"

I'm running into the same problem. Is this a bug? Calling Session.clear() should clear all objects out of the Session, correct? So, why is it finding and complaining about a duplicate collection?

解决办法: After letting this problem sit for a while, I found a workaround. I still believe the test case demonstrates a real (if minor) defect with Hibernate, but there is a simple workaround.

The problem is due to the session "seeing" the same collection twice in the same session (which happens when you update() the collection-containing object after clearing the session). So, the solution is to replace the original collection with a new collection:

load.setMessages(new ArrayList(load.getMessages()));

This will cause some extra updates, but it's a very small price to pay in my case. (I'm inserting upwards of 5 million records into the database in a single transaction, which is why I have to clear() the session periodically in the first place.)

Hope someone else may get benefit from this solution.
The exception can happen (at least) in such case that:
- Your object have Set field, and
- your code does session.update() after session.clear() //This is the key point

(Sorry, I do not know why Hibernate cannot handle this case better)

So, you can for example solve the problem by
- avoid the above case, or
- after session clear, do session.load() to load a clean object again, or
- close the session after session.update(), then you need to open a new session()

I do not justify these quick solutions well. So, you need to do this by yourself. There are probably some other (better) solutions.


I was running into the caching issue too. I have a HibernateUtility class that handles Hibernate sessions for threads using ThreadLocal variables. Since my server opens a bunch of threads, then leaves them open to service future requests, the cache for one thread would be updated, but all other threads would have old data. As a temporary fix, I added a clear(); to the HibernateUtility class to clear the session every time it was retrieved. That's when I ran into the problem described here (call to update() after call to clear() produces an error). I fixed this by passing the HttpServetRequest into the HibernateUtility class and storing its hash in a ThreadLocal variable. If a session is retrieved twice in the same request, it is only cleared the first time. This may not be the best way, but it's easy and it works!

blacksky115 2009-06-16
  • 打赏
  • 举报
回复
我找了很多资料,没有什么很好的解释,其中这篇文章帮助最大http://opensource.atlassian.com/projects/hibernate/browse/HHH-509。
bojianyu 2008-05-05
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 nanjg 的回复:]
<one-to-one name="classBean" class="com.doudou.pojo.ClassBean" cascade="save-update" property-ref="teacher" /> 去掉
[/Quote]

谢谢您,为什么我要把这个去掉呢?这个是教师所带的班级呀,唯一关联。如果我把这个去掉的话,那我就不能在教师登陆后关联得出这个教师是哪个班级的班主任了(只能再查询一次获得了)


===================student.hbm.xml=====================
student.hbm.xml
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="com.doudou.pojo.Student" table="student">
<id name="sid" type="java.lang.Integer">
<column name="sid" />
<generator class="native" />
</id>
<many-to-one name="classBean" class="com.doudou.pojo.ClassBean" cascade="none">
<column name="classId" not-null="false" />
</many-to-one>
<property name="sname" type="java.lang.String">
<column name="sname" length="15" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="10" not-null="true" />
</property>
<property name="gender" type="java.lang.String">
<column name="gender" length="4" not-null="true" />
</property>
<property name="interest" type="string" column="interest"/>
<property name="tel" type="java.lang.String">
<column name="tel" length="15" />
</property>
<property name="address" type="java.lang.String">
<column name="address" length="30" />
</property>
<set name="subjects" table="stu_sub" inverse="true" cascade="save-update" lazy="true">
<key>
<column name="stuId" />
</key>
<many-to-many class="com.doudou.pojo.Subject" column="stusubId" />
</set>
<set name="teachers" table="tea_stu" inverse="true" cascade="none">
<key>
<column name="stuId" />
</key>
<many-to-many class="com.doudou.pojo.Teacher" column="teaId"/>
</set>
</class>
</hibernate-mapping>



===========================================
问题好像出在我修改教师信息的功能上,当我修改教师时,出现a different object with the same identifier value was already associated with the session,因此我使用了session.clear(),这个异常问题解决了,但是上述问题就出现了,查了一些资料,问题就是因为我session.clear()引起的。
请问我该怎么解决这个问题?
谢谢大家!




bojianyu 2008-05-05
  • 打赏
  • 举报
回复
不会吧,好不容易等了一个回复,还是广告,我汗!!
这个群我很早就加了啊,好像没学到东西吧,整天一群人在里面鬼侃。唉
nanjg 2008-05-04
  • 打赏
  • 举报
回复
学生.hbm.xml: 呢?
nanjg 2008-05-04
  • 打赏
  • 举报
回复
<one-to-one name="classBean" class="com.doudou.pojo.ClassBean" cascade="save-update" property-ref="teacher" /> 去掉
Shine_Panda 2008-05-04
  • 打赏
  • 举报
回复
估计可能是
Criteria cri=session.createCriteria(Teacher.class);
cri.add(Expression.and(Expression.eq("tid", teacher.getTid()), Expression.eq("password", teacher.getPassword())));
List teachers = cri.list() ;
这段代码的问题。
可惜我只会用hql. QBC 不懂。 帮不了你。
帮你顶上去把。。。。。。。。。。。。。
bojianyu 2008-05-04
  • 打赏
  • 举报
回复
=================== 1 ===============================
Teacher.java:
public class Teacher implements Serializable{

private int tid; //教师帐号
private String tname; //姓名
private String password; //密码
private String gender; //性别
private String address; //地址
private int grade; //级别(教师分为普通教师和管理员两种)

private ClassBean classBean; //我规定一个教师只能带领一个班级(类似于班主任的意思)
private Set<Student> students; //这个教师做为任课老师可以带很多个学生(可以是不同班级的学生)

/*
构造方法、getter/setter省略
*/

}

=================== 2 ===============================

Teacher.hbm.xml:
<?xml version="1.0" encoding="gb18030"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="com.doudou.pojo.Teacher" table="teacher">
<id name="tid" type="java.lang.Integer">
<column name="tid" />
<generator class="native" />
</id>
<property ....普通属性省略.... />

<!--教师和学生是多对多的关系,tea_stu是中间表-->
<set name="students" table="tea_stu" inverse="true" cascade="none" lazy="true">
<key>
<column name="teaId" />
</key>
<many-to-many class="com.doudou.pojo.Student" column="stuId" />
</set>
<!--教师和班级是唯一外键的关系-->
<one-to-one name="classBean" class="com.doudou.pojo.ClassBean" cascade="save-update" property-ref="teacher" />
</class>
</hibernate-mapping>

=================== 3 ===============================

HibernateUtil.java:
public class HibernateUtil {

private static SessionFactory sessionFactory = null ;
private static ThreadLocal<Session> threadLocal = null ;

static {
threadLocal = new ThreadLocal<Session>() ;
sessionFactory = new Configuration().configure().buildSessionFactory() ;
}

public static Session currentSession(){
Session session = threadLocal.get() ;
if(session==null){
session=sessionFactory.openSession() ;
threadLocal.set(session) ;
}
return session ;
}

public static void closeSession(){
Session session = threadLocal.get() ;
threadLocal.set(null) ;
if(session!=null){
session.close() ;
}
}
}

=================== 4 ===============================

过滤器:HibernateFilter
public class HibernateFilter implements Filter {

public static ThreadLocal<Session> threadLocal = new ThreadLocal<Session>() ;

public void destroy() {
}

private static SessionFactory getSessionFactory(){
Configuration config = new Configuration().configure() ;
return config.buildSessionFactory() ;
}

//关闭session
private static void closeSession(){
Session session = threadLocal.get() ;
if(session!=null){
threadLocal.set(null) ;
if(session.isOpen()){
session.close() ;
}
}
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

//获取session
Session session = threadLocal.get() ;
if(session==null){
//打开session
threadLocal.set(getSessionFactory().openSession()) ;
session = threadLocal.get() ;
}
Transaction trans = session.beginTransaction() ;

chain.doFilter(request, response) ;

//提交事务,关闭session
session.beginTransaction().commit() ;
session.flush() ;
session.clear() ;
closeSession() ;


}

public void init(FilterConfig arg0) throws ServletException {
}

}

=================== 5 ===============================

DAO实现类中修改教师的方法:
//修改特定老师的信息
public boolean updateTeacher(Teacher teacher) {
try{
session = HibernateFilter.threadLocal.get() ;
session.clear() ;
//session.refresh(teacher) ;

session.saveOrUpdate(teacher) ;
//session.evict(teacher) ;
}catch(Exception e){
e.printStackTrace();
return false ;
}
return true ;
}


Service实现类中修改教师的方法:
// 修改自己的资料
public boolean updateTeacher(Teacher teacher) {

if(teacher!=null && teacher.getTid()!=0){
teacher.setClassBean(getClassBeanByTid(teacher.getTid())) ;
//teacher.setStudents(new HashSet()) ;
}

TeacherDAO teacherDAO = (TeacherDAO)DAOFactory.getInstance(DAOType.TEACHER) ;
return teacherDAO.updateTeacher(teacher) ;
}

********************************************************************
在修改之前,我调用了登陆验证方法(下面这个方法是TeacherDAOImpl中的):
//根据教师部分信息获取此教师的详细信息
public Teacher getTeacher(Teacher teacher) {
session = HibernateFilter.threadLocal.get() ;
Criteria cri=session.createCriteria(Teacher.class);
cri.add(Expression.and(Expression.eq("tid", teacher.getTid()), Expression.eq("password", teacher.getPassword())));
List teachers = cri.list() ;
if(teachers!=null && teachers.size()>0){
return (Teacher)teachers.get(0);
}else{
return null ;
}
}
********************************************************************



不胜感激各位的帮忙!!



Shine_Panda 2008-05-04
  • 打赏
  • 举报
回复
能把你的hql 或相关代码贴出来吗?
bojianyu 2008-05-04
  • 打赏
  • 举报
回复
自己先顶一下

67,537

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧