Hibernate的save(transientInstance)引发的持久化对象三种状态思考

来源:互联网 发布:淘宝页面显示不全 编辑:程序博客网 时间:2024/05/23 19:51

一、 背景

    今天同学在做spring+Hibernate的过程中出现一个问题:

 

12-10-09 09:48:46 | ERROR | org.hibernate.jdbc.BatchingBatcher   61 | Exception executing batch:

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1

查看控制台输出异常栈定位了错误是在DAO中执行getHibernateTemplate().save(transientInstance);出错,而调用此操作的逻辑如下:

1. 查询满足条件的属性的List

getHibernateTemplate().find(queryString, cataID);

2. 逐个修改List中元组的部分属性内容

3. 删除原List对应的持久化对象

   getHibernateTemplate().delete()

4. 将修改属性后的List对象逐个持久化保存到数据库中

   getHibernateTemplate().save()

二、错误解决过程

1. google了一下发现很多人有同样的问题,即

   1> 使用的是hibernate的saveOrUpdate方法保存实例(save方法其实底层也是用saveOrUpdate实现)。saveOrUpdate方法要求ID为null时才执行SAVE,在其它情况下执行UPDATE。在保存实例的时候是新增,但你的ID不为null,所以使用的是UPDATE,但是数据库里没有主键相关的值,所以出现异常。

      这种问题的解决方法如下:

      a. 修改主键增长策略increment(参考:google大部分博客里面都有)

      b. 使用unsaved-value=null,避免在id不为null时定向到undate操作,而执行save操作(参考资料 ) 

 

   2> 尝试了上面的解决方案后,问题依旧。我把执行逻辑进行了拆解,将delete和save分成两个函数,每个部分都执行find操作获取List,结果发现错误消失了,删除操作也执行成功了,但save操作未执行(showsql时,未生成insert语句)。这就意味着问题不是出在主键增长策略上,经查发现之前那个问题出在对Hibernate的可持久化对象三种状态不熟悉且未能与Hibernate的各种操作的对象要求结合起来(三态见链接),find获取的对象属于游离态,经过delete后在数据库中删除,接着执行save的对象还是游离态,两个操作的对象是同一个,所以会出现StaleStateException问题,而经过拆解后未操作相同的对象后,因为save持久化对象可临时态,而当前为游离态故无法持久化保存(未生成对应的sql)。类似问题见链接

 

   3> 最终解决办法是new一个对象(该对象为临时态),然后将处于游离态的对象内容copy给新new的对象,然后将new的对象save,问题解决!!

三、几点收获

1. 实践驱动、问题驱动学习确实比单看书要强N倍;

2. 纠错前先思考

3. 程序中bug无处不在,场景一遍问题会出很多,要有思想准备。

4. Hibernate中可持久对象三种状态的转换(使用场景)的理解很重要!!

 

原创粉丝点击