JPA / Hibernate实体类和同步的最佳实践是什么?

时间:2008-10-08 16:23:39

标签: java multithreading hibernate jpa

似乎我见过的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; }
}

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

5 个答案:

答案 0 :(得分:3)

在我看来,你绝不应该跨线程共享域对象。事实上,我通常在线程之间共享很少,因为这些数据必须受到保护。我已经建立了一些大型/高性能系统,从来没有打破这个规则。如果需要并行化工作,请执行此操作,但不要共享域对象的实例。每个线程都应该从数据库中读取数据,通过改变/创建对象对其进行操作,然后提交/回滚事务。工作传入/传出通常应该是值/只读对象。

答案 1 :(得分:2)

“并且可以跨多个线程调用这些方法(虽然这可能是不寻常和奇怪的。)

我只能说休眠,因为我对JPA没有多少经验。 您调用set的对象并不总是与您调用get的对象相同。当Hibernate为您加载Object时,它会调用set *方法,然后您(以及您的线程)调用将获得剩余的时间。

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

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

因此,如果您使用简单的POJO会遇到并发问题,那么您也可以将它们与您的实体一起使用!

在我看到的所有系统中,域模型不是threadsave,实体实例不会被多个线程访问。

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

答案 4 :(得分:0)

您的问题的最佳做法是乐观锁定:

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

  

乐观锁定是一种技术   用于确保更新   对应的数据库数据   实体的状态仅在何时生成   没有更新干预交易   那个实体状态的数据   阅读了实体国家。这个   确保更新或删除   这些数据与   数据库的当前状态和那个   干预更新不会丢失。

您需要在类中添加@Version注释,在数据库表中添加一列。