天天看点

a different object with the same identifier value was already associated with the session 产生原因分析

今天做WOSS项目的时候修改那一模块突然给我抛了个org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.briup.dao.bean.Product#1008] 一看一头2个大 ,这什么破异常 ,从没遇到过嘛

遇到该异常感觉无从下手

 从字面上的意思来看是2个不同的对象关联到了同一个标志位,也就是说在一个session中存在2个对象但这

 2个对象的标志位是相同的比如2个对象的 id值是一样的 而这又是数据库的唯一主键

 在做更新操作的时候就出现冲突了,因为Hibernate不知道到底要去更新哪个对象

 出现这样的问题头都大了,后来 仔细分析了下,做前面模块的时候测试是通过了的,那为什么后面就不行了呢?

 没办法只好将原来的版本拿过来重新部署测试,发现在页面提交修改数据后台做保存的时候不在抛这个异常了

 将数据库发生变化导致异常的情况排出,唯一的可能就是前面做的代码被修改过,所以导致后面在去回归测试的时候

 出现异常,通过比较2个版本更新模块的不 同之处后发现问题暴露出来了

 DAO层方法

 public void modifyProduct(Product product) throws Exception {

  session = HibernateSessionFactory.getSession();

  session.update(product);

 }

 public Product findProductByName(String name) throws Exception {

  session = HibernateSessionFactory.getSession();

  query = session.createQuery("from Product where name='" + name + "'");

  return (Product) query.uniqueResult();

 }

 看下面的业务逻辑方法

 public void modifyProduct(Product product) throws AdminServiceException {

  HibernateTransaction tran = new HibernateTransaction();

  tran.beginTransaction();

  try {

   dao.modifyProduct(product);

   tran.commit();

  } catch (Exception e) {

   tran.rollback();

   e.printStackTrace();

   throw new AdminServiceException(

     FinalErrorMsg.DATA_BASE_UPDATE_ERROR);

  }

 }

 public void modifyProduct(Product product) throws AdminServiceException {

  Product pro = null;

  pro = findProductByName(product.getName());

  if (pro == null) {

   throw new AdminServiceException(

     FinalErrorMsg.MODIFY_PRODUCT_FAIL_BECAUSE_NOT_EXISTED);

  }

  HibernateTransaction tran = new HibernateTransaction();

  tran.beginTransaction();

  try {

   dao.modifyProduct(product);

   tran.commit();

  } catch (Exception e) {

   tran.rollback();

   e.printStackTrace();

   throw new AdminServiceException(

     FinalErrorMsg.DATA_BASE_UPDATE_ERROR);

  }

 }

 当Struts框架在调业务逻辑方法修改页面传过来的时候数据的时候 会去调modifyProduct方法进行修改

 在新版本中就有个非常隐蔽的错误了,注意看在同一个业务逻辑方法中调了2次DAO层的方法,如果要修改的对象在

 数据库中还存在就有个问题产生了,该方法获得的session管理了一个名为pro的对象而这个对象的id和方法传入的

 product的id是一样的所以 当在本方法中再去调DAO层的modifyProduct方法的时候获得的session是和调

 findProductByName这个业务方法获得的一个session是同一个 ,因为我把session的关闭操作放到页面请求

 处理结束response回去的时候通过那个字符编码转化的filter过滤的是执行完doFilter方法后做的关闭

 Hibernate Session操作来实现提交数据到数据库的 所以最终在这个修改的业务逻辑中使用2次Dao层的方法

 而获得的是同一个session 所以要修改一个和该session管理的一个对象一样的identifier的对象会出现冲突.

解决办法有在该方法中执行dao.modifyProduct(product);前先将session关闭 调用Session的 close方法

Session的缓存被清空,其中的所有持久化对象都变为游离对象,

调用Session的evict()方法能够从缓存中删除一个持久化对象,使其变为游离状态,这样也能达到解决同一个

session中关联2个同一个identifer的对象更新的时候产生的冲突

 问题原因找到解决就好办了,只要把要考虑并发情况出现的用户要修改的数据可能在提交后是否还存在的数据验证放到struts调用该方法的Action中去判断 就行了

从设计思想上看 修改的业务逻辑方法 还是做了它不该做的事就是去判断被修改的对象是否还存在,这个应该交给调用它的上层逻辑去判断

还有看来还是要老实的做单元测试,写好某层的某方法就先做单元测试免得让错误影响到后面的实现,最终导致一时半会找不到错误的源头