2

我见过的大多数 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; }
}

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

4

5 回答 5

3

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

于 2009-04-16T21:15:44.957 回答
2

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

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

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

于 2008-10-08T17:52:45.447 回答
1

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

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

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

于 2008-10-14T13:08:35.940 回答
0

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

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

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

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

于 2009-02-18T12:34:36.870 回答
0

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

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

乐观锁定是一种技术,用于确保仅当自读取实体状态以来没有干预事务更新实体状态的数据时,才对与实体状态相对应的数据库数据进行更新。这确保了对该数据的更新或删除与数据库的当前状态一致,并且干预更新不会丢失。

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

于 2009-03-25T16:59:23.900 回答