1、仅仅Hibernate情况:
net.sf.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: .........
这样的问题不知道大家有没有遇到?
SessionFactory sessionFactory= new Configuration().configure().buildSessionFactory();
Session session=sessionFactory.openSession();
User user1=new User("anray");
User user2=new User("anray");
user1.setName("anray1");//假设User是一个持久对象,有一个name属性
user2.setName("anray2");
session.update(user1);
session.update(user2);
这样就会出现上面的异常
看上去上面的代码有点傻,我们编程中不可能出现这样的情况。
但是有一种情况是经常用到的,就是写一个专门的类来管理sessionFactory和session,如下
/*
* Examination System version 0.1
* Data persist layer(Using Hibernate 2.1.2)
* Created on 2004-4-9 11:58:12
*/
package bts.hibernate;
/**
* @author Anray
*/
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public class HibernateUse
{
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException("Exception building SessionFactory: " + ex.getMessage(), ex);
}
}
public static final ThreadLocal threadLocal = new ThreadLocal();
/**得到当前线程的session */
public static final Session getSession() throws HibernateException {
Session se = (Session) threadLocal.get();
// Open a new Session, if this Thread has none yet
if (se == null) {
se = sessionFactory.openSession();
threadLocal.set(se) ;
}
return se;
}
/**关闭当前线程的session */
public static final void closeSession() throws HibernateException {
Session s = (Session) threadLocal.get();
threadLocal.set(null);
if (s != null)
s.close();
}
/**
*保存一个对象到数据库中,使用完后要自己调用closeSession()
*/
public static void create(Object object)
throws HibernateException{
Session session1 = getSession();
Transaction transaction1 = session1.beginTransaction();
try{
session1.save(object);
transaction1.commit();
}catch(HibernateException ex){
if(transaction1!=null)transaction1.rollback();
closeSession();
throw ex;
}
}
/**
*更新一个对象到数据库中,使用完后要自己调用closeSession()
*/
public static void update(Object object)
throws HibernateException{
Session session1 = getSession();
Transaction transaction1 = session1.beginTransaction();
try{
session1.update(object);
transaction1.commit();
}catch(HibernateException ex){
if(transaction1!=null)transaction1.rollback();
closeSession();
throw ex;
}
}
}
如果在web编程时用这个类的方法来更新一个user对象,在调用了HibernateUse.update(user)后,没有接着调用HibernateUse.closeSession(),其实为了节省资源也不提倡每次操作都调用HibernateUse.closeSession()关闭session;如果对相同的user对象进行又一次的更新时,碰巧容器又分配了同一个thread进行处理,那么得到的也是同一个session,这样就出现了上面的提到了问题,session里的两个user的识别id一样,但内容不一样。
为了解决这个问题可以在,update前清空session(不是关闭,这样资源消耗比较少),修改后的公用类如下:(就是在update()方法里添加了session1.clear();)
/*
* Examination System version 0.1
* Data persist layer(Using Hibernate 2.1.2)
* Created on 2004-4-9 11:58:12
*/
package bts.hibernate;
/**
* @author Anray
*/
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public class HibernateUse
{
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException("Exception building SessionFactory: " + ex.getMessage(), ex);
}
}
public static final ThreadLocal threadLocal = new ThreadLocal();
/**得到当前线程的session */
public static final Session getSession() throws HibernateException {
Session se = (Session) threadLocal.get();
// Open a new Session, if this Thread has none yet
if (se == null) {
se = sessionFactory.openSession();
threadLocal.set(se);
}
return se;
}
/**关闭当前线程的session */
public static final void closeSession() throws HibernateException {
Session s = (Session) threadLocal.get();
threadLocal.set(null);
if (s != null)
s.close();
}
/**
*保存一个对象到数据库中,使用完后要自己调用closeSession()
*/
public static void create(Object object)
throws HibernateException{
Session session1 = getSession();
Transaction transaction1 = session1.beginTransaction();
try{
session1.save(object);
transaction1.commit();
}catch(HibernateException ex){
if(transaction1!=null)transaction1.rollback();
closeSession();
throw ex;
}
}
/**
*更新一个对象到数据库中,使用完后要自己调用closeSession()
*/
public static void update(Object object)
throws HibernateException{
Session session1 = getSession();
Transaction transaction1 = session1.beginTransaction();
try{
session1.clear();
session1.update(object);
transaction1.commit();
}catch(HibernateException ex){
if(transaction1!=null)transaction1.rollback();
closeSession();
throw ex;
}
}
}
2、Spring中的Hibernate事务代理遇到该情况
参考:http://hi.baidu.com/fgfd0/blog/item/fe258935b74cee1790ef39ba.html
注意:我这里解决的是:org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session:
网上大多数解决的是 :net.sf.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
他们也是有区别的,因为我用的是Spring中的Hibernate事务代理,来自动管理事务的。
Hibernate session中对象重复问题的解决方法(结合Spring应的Hibernate事务管理)
在网上查到的都是单针对Hibernate session对象重复的问题,没有查到在结合Spring事务代理时的Hibernate session对象重复问题的解决方法。
我遇到的问题是,我使用了org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
所以在:下面的配置中userInfoService等service的事务都是自动的,OK自动事务是我想要的,我需要在对多个对象或多个表操作时在Service层的事务管理,
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>userInfoService</value>
<value>adminInfoService</value>
<value>pageService</value>
<value>companyInfoService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
但是有时会出现问题也就是:NonUniqueObjectException: a different object with the same identifier value was already associated with the session 异常。
并且经常会出现,这个异常很明显是因为Hibernate session出现了重复的对象,所以Hibernate不能同时更新两个拥有同一ID的对象。
具体详细问题我就不多说是怎么回事了可以看:http://www.cjsdn.com/post/view?bid=2&id=104169
下面是具体的解决方法:这个方法cAwardUpdateUserInfo,要求保存ui1和ui2并且查询他们的子如果他们有子对象责更新子。
public UserInfo cAwardUpdateUserInfo(UserInfo ui1, UserInfo ui2) {
//最新修改的写法..... 也就是把所有的更新都放在最上面,下面的对象如果有改变Hibernate会自动更新他们,如果没改变Hibernate责不会管他。
//这里为什么能解决问题我估计是因为手动调用update应该会自动清空session,所以这时session就不会有重复对象了,那下面的更新也就不用我们来管理了,并给Hibernate事务管理吧。
this.saveUpUserInfo(ui1);
this.saveUpUserInfo(ui2);
//这里是出现问题的地方,也就是根据每个对象查出他们的子对象,如果有子对象就更新,
//没子对象就不更新,所以假设查到子对象了,并更新了子对象。那它的父对象也能也更新。
List ui1List = this.findUserInfoAllCAwardSub(ui1.getId()+"");
List ui2List = this.findUserInfoAllCAwardSub(ui2.getId()+"");
//开始 父子调换
int subFather = ui1.getUiAwardCjFather();
int subFatherCj = ui1.getUiAwardCj();
int subFatherAdd = ui1.getUiAwardAddSum();
ui1.setUiAwardCjFather(ui2.getUiAwardCjFather());
ui1.setUiAwardCj(ui2.getUiAwardCj());
ui1.setUiAwardAddSum(ui2.getUiAwardAddSum());
if(ui2List!=null && ui2List.size()>=1){
System.out.println("ui2List:"+ui2List.size());
for(int i=0; i<ui2List.size(); i++){
UserInfo tempSubUi = (UserInfo)ui2List.get(i);
tempSubUi.setUiAwardCjFather(ui1.getId());
}
}
ui2.setUiAwardCjFather(subFather);
ui2.setUiAwardCj(subFatherCj);
ui2.setUiAwardAddSum(subFatherAdd);
if(ui1List!=null && ui1List.size()>=1){
System.out.println("ui1List:"+ui1List.size());
for(int i=0; i<ui1List.size(); i++){
UserInfo tempSubUi = (UserInfo)ui1List.get(i);
tempSubUi.setUiAwardCjFather(ui2.getId());
}
}
return ui1;
// 下面是最起初 错误的写法。
// if((ui2List!=null && ui2List.size()>=1) && (ui1List!=null && ui1List.size()>=1)){
// System.out.println("进入全不保存");
// return ui1;
// }
//
// if((ui1List!=null && ui1List.size()>=1) && (ui2List==null || ui2List.size()<=0)){
// System.out.println("进入保存ui1");
// //保存是否成功!
// if(this.saveUpUserInfo(ui2)!=null){
// return ui1;
// }else{
// return null;
// }
// }
//
// if((ui2List!=null && ui2List.size()>=1) && (ui1List==null || ui1List.size()<=0)){
// System.out.println("进入保存ui2");
// //保存是否成功!
// if(this.saveUpUserInfo(ui1)!=null){
// return ui1;
// }else{
// return null;
// }
//
// }
//
// System.out.println("进入全保存!");
// //保存是否成功!
// if(this.saveUpUserInfo(ui1)!=null && this.saveUpUserInfo(ui2)!=null){
// return ui1;
// }else{
// return null;
// }
}