hibernate - 如何使 Hibernate @DynamicUpdate 与两个更新线程一起工作?

标签 hibernate spring-data

我想在我的@Entity 上使用@DynamicUpdate,因为我有两个线程可以更改同一行上的不同列,并且在没有动态查询生成的情况下,所有列都在每次更新时写入,第二个线程覆盖第一个线程写入的内容一。

例子:

@Entity Task has attributes A and B set to false.
- Thread 1 loads Task#1 from the database and starts some long operation outside transaction
- Thread 2 loads Task#1 from the database and sets A=true, then saves
> the database holds Task#1 with A=true and B=false
- Thread 1 sets B=true, then saves
> the database holds Task#1 with A=false (!!!) and B=true
I was expecting to have A=true but it was overwritten with the 
original value when Thread 1 saved

问题在于,仅在 @Entity 上添加 @DynamicUpdate 不会更改查询,它仍在更新所有列。
更新 :我设法通过使用 @DynamicUpdate 注释所有类层次结构来完成这项工作(不仅仅是像以前那样保存属性的基类),但我仍然有问题(见下文)

我将 Spring Data JPA 1.10.2 与 Spring 4.2.6 和 Hibernate 5.1.0 一起使用。
一个线程使用 JpaRepository 上的自定义查找来获取实体,另一个线程使用同一 JpaRepository 上的 findOne() 获取实体。
最后都在 JpaRepository 上调用 save() 。
只有JpaRepository 是@Transactional,所以读取和保存操作发生在不同的事务中。

我也尝试添加@SelectBeforeUpdate,但没有任何区别。

另外,是否有任何关于如何使用动态查询生成的文档?我只在 javadoc 中找到了两行但用户指南中没有任何内容。

或者也许我应该完全使用不同的方法?任何解决方法?我只能想到用 native sql 分别更新每一列,但这会很伤心。

更新:
现在我有了动态更新,理论上只保存更改的列。
但实际上,在一个线程上标记为已更改的列也由第二个线程存储!
这是更详细的情况:

我的实体被称为 Task 并且具有“id”、“priority”、“executionTime”和“foo”属性。
它被子类化了两次:BigTask 扩展了 Task,HugeTask 扩展了 BigTask,但我在处理实例时使用了 Task。
所有类都使用@Entity 和@DynamicUpdate 进行注释。
在“setPriority()”方法中,我添加了一个调试行,用于打印前一个和新的优先级值。

使用 Web 控制台,我启动了一个任务实例:启动了一个新的异步线程,该线程从数据库加载任务并在事务之外启动一些长时间运行的操作。
在任务运行时,我在 Web 控制台上更改其优先级并保存。我看到调试消息“优先级从 10 更改为 20”并且数据库保存了新值。 hibernate 记录的更新查询是
update Task set priority=? where id=?

这是正确的,表明 @DynamicUpdate 工作正常。
几分钟后,异步线程终止其任务,设置 executionTime 并保存。现在是 hibernate 查询
update Task set executionTime=?, priority=? where id=?

这不是预期的。 @DynamicUpdate 正在使用(“foo”属性不包含在查询中)但“优先级”显然已被标记为已修改,即使它的 setter 尚未被调用。并且它永远不会在代码中直接访问。

对我来说看起来很奇怪:两个线程使用的任务实例是不同的(或者两个更新都会设置新的优先级)但显然“脏标志”是共同的!真的是这样吗?

最佳答案

您遇到的问题通常可以通过使用某种形式的 locking 来解决。为了避免两个线程同时更改同一行。

一般来说,您通常有两种锁定方法:乐观或悲观。

乐观
这种类型的锁定涉及引入持久性提供程序使用的特定字段,以确定在更新时是否有其他事务在当前线程上次读取实体后更改了该实体。如果持久性提供程序检测到这种情况,它将抛出异常,让您处理它。在这种情况下,您可以重新读取实体状态并重新应用必要的更改并尝试再次保存或抛出您自己的异常。

悲观
这种类型的锁定将锁定控制向下游推送到数据库,您在线程 1 读取数据时应用行级锁定,防止任何其他线程能够读取该行,直到第一个线程提交它的数据。虽然这似乎是表面上想要的,但这种方法通常不能很好地扩展,特别是在多个线程/事务很可能需要在接近的时间访问和操作相同的行的情况下。

关于hibernate - 如何使 Hibernate @DynamicUpdate 与两个更新线程一起工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38161628/

相关文章:

java - Hibernate 只获取单个子实例

java - 如何获取通过 Hibernate 连接的数据库名称?

java - 如何注释 Hibernate 实体以同时支持 Java 和数据模型?

java - 我怎样才能实现这个 REST API 并保持 DRY?

java - Spring Data Rest 按关键字搜索

c# - NHibernate 内连接仅获取某些列?

java - 找不到 persistence.xml

java - spring-data-cassandra 未找到 UserDefinedType

java - Spring Batch 无法打开 JPA EntityManager 进行事务;嵌套异常是 java.lang.IllegalStateException : Transaction already active

java - Spring Data JPA Projection 从数据库中选择的字段