环境
hibernate4.2
ojdbc6-Oracle 11.2.0.3.0 JDBC 4.0
Oracle数据库11g
问题
我们遵循了许多建议,以如下方式配置Hibernate批处理:
<property name="hibernate.jdbc.batch_size">100</property> <property name="hibernate.order_inserts">true</property> <property name="hibernate.order_updates">true</property> <property name="hibernate.jdbc.batch_versioned_data">true</property>
我们检查了日志,发现生成的SQL语句已批处理。但是,如果两个事务同时修改相同版本的实体行,则Hibernate将成功提交它们,导致最后提交的事务中的冲突更新丢失(两个事务中都保存了无冲突的数据,因此最后一个事务离开了数据库)处于不一致状态)。
令人惊讶的是,关于此行为的文档很少。Hibernate官方文档说:
hibernate.jdbc.batch_versioned_data 如果您的JDBC驱动程序从executeBatch()返回正确的行数,则将此属性设置为true。通常可以安全地打开此选项。然后,Hibernate将使用批处理DML来自动版本化数据。默认为false。
hibernate.jdbc.batch_versioned_data
如果您的JDBC驱动程序从executeBatch()返回正确的行数,则将此属性设置为true。通常可以安全地打开此选项。然后,Hibernate将使用批处理DML来自动版本化数据。默认为false。
通常安全 吗?在通知整个版本已损坏之前,我们几乎将其发送到了生产环境。
我们搜寻了五年前发布的博客,描述了这种怪异。显然,Hibernate很长时间没有对此做任何事情。
Hibernate为何如此表现?它从jdbc驱动程序获得信息,即更新的行数未知,为什么它不抛出异常来指示它,却给人留下版本检查已成功通过的印象?
oracle驱动程序应返回正确的行数。如果不是这种情况,我会感到惊讶。您能够确认驱动程序的结果正确吗?您可以打开Hibernate日志记录进行检查。
需要检查的几件事:
记录发送到数据库的实际SQL,并检查where子句中是否提到了version列。不确定是否通过批处理启用的Hibernate登录来记录SQL,然后您可能不得不采用另一种方式记录SQL(例如,p6spy)
如果在并发更新期间正确返回了行计数,则该应用程序运行正常。通过检查版本列的值是否已更新来更正此内容。
更新 根据以下链接,此问题在11g之前的Oracle驱动程序中一直存在,并已在版本12c中修复
https://hibernate.atlassian.net/browse/HHH-3360
对于以前的Oracle版本,还有一些其他有用的信息,即提供了自定义解决方案。
其他资源:https: //hibernate.atlassian.net/browse/HHH-5070