【问题标题】:What is the best practice for JPA/Hibernate entity classes and synchronization?JPA/Hibernate 实体类和同步的最佳实践是什么?
【发布时间】:2010-09-16 01:35:16
【问题描述】:

我见过的大多数 JPA/Hibernate 实体 bean 类的示例似乎都没有显式同步。然而,可以在构建事务的上下文中调用这些对象的 getter/setter。并且有可能跨多个线程调用这些方法(尽管这可能是不寻常和奇怪的)。

似乎如果它是跨多个线程构建的,那么对对象状态的更改可能会丢失,这将是可悲的。

那么,省略同步是最佳实践吗? Hibernate 检测代码是否为我处理了正确的同步?

举个例子:

@Entity
public class Ninja {
  @Id @GeneratedValue
  private Long id;

  @Column 
  private String name;

  @Column
  private int throwingStars;

  public Ninja() {}
  public int getThrowingStars() { return throwingStars; } 
  public void addThrowingStar() { throwingStars += 1; }
}

投掷星的方法需要同步吗?我当然不希望我的忍者失去任何投掷的星星。

【问题讨论】:

  • "[...] 对象状态的更改会丢失,这将是可悲的。"

标签: java multithreading hibernate jpa


【解决方案1】:

在我看来,您永远不应该跨线程共享域对象。事实上,我通常在线程之间共享很少,因为必须保护这些数据。我已经构建了一些大型/高性能系统,并且从未违反过这条规则。如果您需要并行化工作,请这样做,但不要通过共享域对象的实例。每个线程都应该从数据库中读取数据,通过改变/创建对象对其进行操作,然后提交/回滚事务。 Work pass in/out 一般应该是 value/readonly 对象。

【讨论】:

    【解决方案2】:

    解决问题的最佳实践是乐观锁定:

    来自 Java Persistence API (JPA) 规范(第 3.4.1 章):

    乐观锁定是一种技术 用于确保更新到 对应的数据库数据 一个实体的状态只有在 没有干预交易已更新 实体状态的数据,因为 实体状态被读取。这 确保更新或删除到 该数据与 数据库的当前状态和 干预更新不会丢失。

    您需要为您的类和数据库表中的一列添加一个@Version 注释。

    【讨论】:

      【解决方案3】:

      JPA/Hibernate 实体是 POJO。 Hibernate 和任何 JPA-Provider 都不会改变运行时语义。

      因此,如果您在使用简单 POJO 时遇到并发问题,您的实体也会遇到并发问题!

      在我见过的所有系统中,域模型都不是线程保存的,实体实例不会被多个线程访问。

      但是,您可以同时拥有相同实体的多个实例。在这种情况下,同步是通过 DB 完成的。这里的模式是乐观和悲观锁定。 Hibernate 和 JPA 可以帮助您实现这些模式。

      【讨论】:

        【解决方案4】:

        两个线程的对象可能不同。假设您的线程使用会话工厂来访问数据库,您从会话中获得的对象应该被视为“独立”(我相信 hibernate 会为每个 get() 创建“新鲜”对象,除非它们在会话中)。

        至于你的星星问题,当两个人从数据库中获取同一行时,它是相同的,数据库“ACID”属性将确保每个操作都是原子的,所以如果你从线程 t1 中的忍者中删除一颗星和 commit,线程 t2 将读取 t1 的已提交值。

        或者,您可以要求 hibernate 在 T1 中锁定相关 ninja 的行,这样即使 T2 请求该行,它也必须等到 T1 提交或中止。

        【讨论】:

          【解决方案5】:

          “而且有可能跨多个线程调用这些方法(尽管这可能是不寻常和奇怪的)。”

          我只能代表 hibernate,因为我对 JPA 没有太多经验。 调用 set 的对象并不总是调用 get 的对象。当 Hibernate 为你加载 Object 时,它会调用 set* 方法,然后你(和你的线程)调用 get 剩下的时间。

          同样,当您(即您的写入器线程)修改现有对象然后再次保存它时,您需要保护对对象的访问(以便其他读取器/写入器线程不会读取脏数据)。

          【讨论】:

          • 所以你期望在后面的情况下看到实体类中的同步?还是您希望看到由应用协调的更高级别的同步?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-10-15
          • 2011-06-25
          • 1970-01-01
          • 2011-04-06
          • 1970-01-01
          相关资源
          最近更新 更多