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

如何使Hibernate@DynamicUpdate与两个更新线程一起工作?,hibernate,spring-data,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 - Thr

我想在我的@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注释所有类层次结构(不仅仅是像我以前那样保存该属性的基类)成功地实现了这一点,但我仍然存在问题(见下文)

我在Spring4.2.6和Hibernate5.1.0中使用SpringDataJPA1.10.2。 一个线程正在获取实体,该实体在JpaRepository上具有自定义查找,另一个线程在同一JpaRepository上具有findOne()。 最后,这两个函数都对JpaRepository调用save()。 只有JpaRepository是@Transactional,因此读取和保存操作发生在不同的事务中

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

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

或者我应该使用一种完全不同的方法?有解决办法吗?我只能考虑使用本机sql分别更新每一列,但那将是悲哀的

更新: 现在我有了动态更新,所以理论上只保存更改的列。 但实际上,在一个线程上标记为changed的列也会被第二个线程存储! 更详细的情况如下:

我的实体称为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运行良好。 几分钟后,异步线程终止其任务,设置执行时间并保存。hibernate查询现在已启动

update Task set executionTime=?, priority=? where id=?
这并不像预期的那样@正在使用DynamicUpdate(查询中不包括“foo”属性),但“priority”显然已标记为已修改,即使其setter尚未调用。而且它从未在代码中直接访问过


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

您所经历的通常是通过使用某种形式的来补救,以避免两个线程同时更改同一行

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

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

悲观的

这种类型的锁定将锁控件推到数据库的下游,在线程1读取数据时应用行级锁,防止任何其他线程在第一个线程提交数据之前读取该行。虽然表面上看这似乎是人们想要的,但这种方法通常不能很好地扩展,特别是当多个线程/事务需要在很近的时间内访问和操作同一行时,更是如此。。给任何来这里的人的一小段信息

在我的例子中,我使用JPARepository中的普通findById()修改了我的应用程序,以便在更新之前系统地重新加载实体。这对于需要时间完成但在操作之前检索实体的线程来说特别重要

理论上,这仍然会导致更新丢失,就像在实体刷新和保存之间修改数据一样。更多信息请点击此处:

在无法接受丢失更新的情况下,我添加了乐观锁定(基于版本字段),并拒绝了具有可丢失更新的线程的修改


当这不可能时,我在再次保存之前刷新了StaleStateException捕获中的对象(只需要一次)

很抱歉,可能我描述问题的速度太快了。这两个线程没有同时访问同一行。他们在同一个实体上工作,但在不同的时间访问数据库。我现在正在用更多的细节更新我的问题。也许我可以像你建议的那样解决事务问题,但是我觉得@DynamicUpdate应该是正确的方法,如果它有效的话。我现在遇到了一个非常类似的问题。你能绕过这个问题或解决它吗?不,我放弃了。我找到了一个特定于应用程序的解决方法。我记得不太清楚,但我想我确保了所有线程都在使用同一个实体实例,使用了某种工厂模式和E