【问题标题】:JPA best practice question - Updating one field onlyJPA 最佳实践问题 - 仅更新一个字段
【发布时间】:2010-09-10 18:30:07
【问题描述】:

我的问题类似于

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

【问题讨论】:

  • 参考答案here看JPA 2.1支持部分实体更新

标签: java jpa eclipselink


【解决方案1】:

您应该在 JPA 中使用乐观锁定 (@Version)。这将导致第二个客户端的事务失败。

此外,EclipseLink 只会更新已更改的字段,因此如果您将两个对象都读入事务/持久性上下文,它们只会更新已更改的字段。让它恢复更改的唯一方法是在读取对象后序列化或分离对象,然后合并回新的持久性上下文。 (请注意,merge() 仅适用于分离的对象,不应用于托管对象,因为它们已被托管)。

【讨论】:

    【解决方案2】:

    Thread1 引用 Foo 的分离实例,thread2 引用 Foo 的 diff 实例也是如此,乐观锁定 (@Version) 对你不起作用,因为当你第二次尝试保存分离实例时,它会抛出异常,最好的方法解决您的问题的方法是每次在合并分离实体之前都这样做

    溶胶1: // 每次更改时加载 foo foo = em.find(foodId, Foo.class); //foo.set.. 进行更改 em.merge(foo)

    溶胶2: // foo 指的是分离的实体 em.refresh(foo); //foo.set .. 进行更改 em.merge(foo)

    【讨论】:

      【解决方案3】:

      首先,您可以利用处理此类情况的事务。如果性能不是问题,请使用悲观锁定并解决您的问题 - 第二个线程将等到第一个线程更新值。

      但我同意这个用例应该有一些更简单的东西。您可以使用纯 JDBC 并编写更新查询。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-04
        • 1970-01-01
        • 2013-08-18
        • 1970-01-01
        • 2010-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多