【发布时间】:2016-11-04 19:45:11
【问题描述】:
我想在我的@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 中只找到了两行,但在用户指南中没有找到。
或者我应该完全使用不同的方法?任何解决方法?我只能想到用原生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 并保存。现在是休眠查询
update Task set executionTime=?, priority=? where id=?
这不是预期的。正在使用@DynamicUpdate(“foo”属性不包含在查询中),但“priority”显然已被标记为已修改,即使它的设置器尚未被调用。而且它永远不会在代码中直接访问。
对我来说看起来很奇怪:两个线程使用的任务实例是不同的(或者两个更新都会设置新的优先级)但显然“脏标志”是共同的!真的是这样吗?
【问题讨论】:
-
我现在遇到了一个非常相似的问题。您是否能够解决或解决此问题?
-
不,我放弃了。我找到了一个特定于应用程序的解决方法。我记不太清了,但我想我确保所有线程都使用同一个实体实例,使用某种工厂模式和实体池。好伤心。
标签: hibernate spring-data