org.springframework.orm.ObjectOptimisticLockingFailureException错误查找及解决
目前我们遇到了一个报错信息如下所示:
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:290) ~[spring-orm-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:214) ~[spring-orm-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:519) ~[spring-orm-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:496) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:276) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at com.sun.proxy.$Proxy236.deleteBalancer(Unknown Source) ~[na:na]
一开始,我是拒绝的……
这也能报错,只是个删除的操作,而且不能重现,完全不可理喻,是什么原因呢?
网上(某度)查了一些解决办法,大体归为几类:
1.操作本身问题,例如有设置了自增字段,而又对此表更新时设置了字段值;(这个当然pass掉,因为根本不是更新)
2.使用的是hibernate的saveOrUpdate方法保存实例。saveOrUpdate方法要求ID为null时才执行SAVE,在其它情况下执行UPDATE。在保存实例的时候是新增,但你的ID不为null,所以使用的是UPDATE,但是数据库里没有主键相关的值,所以出现异常;(这个也pass掉,因为不是SaveOrUpdate方法)
3. 在Hibernate映射一对多,多对一,多对多的时候新增常常会出现这个异常。(这个也pass,也不是新增)
各种解释都有,后来找了几个大牛问了一下,大体如下:
1.乐观锁异常,数据库并发导致
2.其他存储过程或者方法锁表,长期没有释放锁,导致报错
3.更新的字段有多个值
第三个解释应该不会,如果存在多个值,那这个记录应该永远都无法删除。
第二个解释和第一个解释,似乎也不太可能,因为翻了一下服务器的日志,没有找到操作这条记录相关的报错信息。
但第一个和第二个解释相对靠谱,于是朝着这个思路往下找。
发现如下现象:
1处为创建
2处为查询
3处为删除
4处删除报错
可以发现时间非常短,而另外有quartz服务器每5秒钟执行一次同步任务。
那么问题就出现了,3处删除时正好是quartz执行任务的时候,二者出现了并发。
即在2处与3处中间时,quartz已经将数据删除了,导致3处再删除时报错。
而日志中没有体现报错的原因是,quartz在删除时根本没有错误,所以一直未发现错误。
至此解决方法就很多了,保证数据一致性即可。