JPA最佳实践问题 - 仅更新一个字段

时间:2010-09-10 18:30:07

标签: java jpa eclipselink

我的问题类似于

Updating one field in JPA entity

基本上我有一个实体Foo,它有一些基本字段(enumStringDateTime),还有另外两个字段。其中一个是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

3 个答案:

答案 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并撰写更新查询。