我的问题类似于
Updating one field in JPA entity
基本上我有一个实体Foo,它有一些基本字段(enum
,String
,DateTime
),还有另外两个字段。其中一个是OneToOne,另一个是使用Collection实现的OneToMany。
我有两个正在运行的线程,并且在不同的时间将使用Singleton EntityManager包装类上的findById类型方法查找同一个实体(我没有写这个;)
我想避免的是以下情况:
线程1查找ID为1的Foo,并在该时刻获得Foo的Foo状态引用。
线程2查找ID为1的Foo并获取与线程1具有相同状态的Foo引用
线程1更改Foo上的String字段并使用合并将其保留 线程2更改foo上Enum字段的值,并使用合并
保留它最后一个操作导致更改线程1被替换为线程2获得的String字段的旧状态,因为合并正在更新所有内容(除了OneToOne和OneToMany,因为它们的CascadeType是Persist和Remove)。
我正在寻找的是建议当一个人只需要以这种方式更新特定字段时,防止这种状态破坏的最佳实践方法是什么。
虽然我有一个,我认为是在帖子顶部的链接中提到的,是改变使用它现在使用的通用save(Object o)
进行合并的东西特定于这种情况,它对从实体ID键控的特定字段执行UPDATE。有没有更好的办法?我当前的持久性提供程序是EclipseLink。
TIA
-NBW
答案 0 :(得分:3)
您应该在JPA中使用乐观锁定(@Version)。这将导致第二个客户的交易失败。
EclipseLink也只会更新已更改的字段,因此如果您将两个对象都读入事务/持久性上下文,它们只会更新它们更改的字段。恢复更改的唯一方法是在对象读取后对其进行序列化或分离,然后将其合并为新的持久化上下文。 (请注意,merge()仅对分离的对象是必需的,不应该用于托管对象,因为它们已经被管理)。
答案 1 :(得分:1)
Thread1引用Foo的分离实例,并且thread2引用Foo的diff实例也是如此,乐观锁定(@Version)将不适合您,因为当您尝试第二次保存分离实例时它将抛出异常,最佳方式解决您的问题是在合并分离的实体之前每次都执行此操作
Sol1: //每次进行更改时加载foo foo = em.find(foodId,Foo.class); //foo.set ..改变 em.merge(富)
Sol2: // foo指的是分离的实体 em.refresh(FOO); //foo.set ..改变 em.merge(富)
答案 2 :(得分:0)
首先,您可以利用处理这些情况的交易。如果性能不是问题,请使用悲观锁定并解决您的问题 - 第二个线程将等到第一个线程更新值。
但我同意这个用例应该有更简单的东西。您可以使用纯JDBC并撰写更新查询。