在测试JPA乐观锁定时,我有简单的实体和相应的服务/控制器。锁定属性如下所示:
@Version
@Column(name = "opt_lock", nullable = false)
private short optLock;测试场景。我有一份已经存在的记录。实体包含ID、optLock和2个数据字段valueA和valueB。这两种方法都被设置为1. .save方法调用上有断点,它被包装在试图捕获所有RuntimeExceptions中。通过TransactionTemplate启动的整个事务也是如此。
在控制器上,我调用PUT方法将值( valueA和valueB )分别更新为2-5.实体按ID读取,只更新值valueA和valueB,没有触摸锁定字段。挂起.save方法调用断点,该方法被配置为只阻塞单个线程。
同样的操作,但是值为200-1。线程也被阻塞。
我取消了第二次更新,验证数据被正确更新到200-1,在已发布的语句中正确发送了200-1值,update语句中使用的optLock值也是数据库中存在于先前记录中的值。对,是这样。
我取消了与第一次更新相关的线程,我看到有人试图用正确的值2-5更新记录,使用了正确的optLock,即。在此操作之前存在于db中的对象。此时,db中不存在组合ID-optLock。因此,无法更新此记录。但事实并非如此。没错!
但是..。也不例外。为什么?
在尝试调试它时,我尝试从项目中删除net.ttddy.数据源--如果它不吞服它(它不会);如果整个场景开始失败--如果我删除了@Version注释(确实如此),我尝试通过hibernate代码进行调试,但是我没有发现任何问题。
有什么建议可以导致缺失异常吗?
更新:用普通的EntityManager替换spring的内容,这是一样的。试图在Intellij IDEA之外运行它,事实证明,它有时可以对您的db调用执行非常令人惊讶的操作,而且也是一样的。更令人惊讶的是(对我来说)如果我引入刷新和清除调用并在它们上放置断点,线程在刷新和清除之后在线挂起,以某种方式锁定持久化上下文或其他东西,但是第二个线程只是挂起直到第一个线程完成,所以这不能用来模拟锁异常,因此我认为如果对持久性上下文的访问是同步的(可能在默认设置中,这将是我当前的设置),这也不会在现实生活中造成问题。
发布于 2020-09-10 13:06:12
问题原因:在我们的例子中,问题是由批处理更新引起的,配置为:
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true并使用buggy甲骨文驱动程序:
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>我们一直在使用它,因为我们的一些产品仍然是在weblogic上,并且为weblogic获得正确的依赖关系并不需要那么容易,所以我们选择不触及起作用的东西。
由于此驱动程序中的错误,jpa乐观锁和批处理更新的组合确实有效,但不会引发异常。如果我们对...batch_size和...batch_versioned_data进行评论,这似乎是可行的。大学甚至认为只需评论一下...batch_versioned_data就行了。
链接:
它似乎不是甲骨文特有的错误,其他dbs也有:Hibernate optimistic locking different behavior between Postgres and MariaDb。
Optimistic locking batch update
Hibernate saves stale data with hibernate.jdbc.batch_versioned_data
解决方案:
正如@Vlad在上面提到的一个链接中所指出的,将oracle驱动程序升级到下面似乎会有所帮助。
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.3.0.0</version>
</dependency>https://stackoverflow.com/questions/63798043
复制相似问题