2008-10-08 86 views
2

看起来像我见过的大多数JPA/Hibernate实体bean类的示例都没有明确的同步。然而,在构建事务的上下文中,可以在这些对象上调用getters/setter。而且这些方法可以跨多个线程调用(尽管也许这是不寻常的和奇怪的)。JPA/Hibernate实体类和同步的最佳实践是什么?

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

那么,是不是同步最佳做法? 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; } 
} 

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

+0

”[[]]对象状态的改变会丢失,这会很难过。“ < - 我喜欢那种轻描淡写(“会很伤心”):-) – dertoni 2012-02-13 09:06:25

回答

1

2个线程的对象可能不一样。假设你的线程使用会话工厂来访问数据库,你从会话中得到的对象应该被看作是“独立的”(我相信hibernate会为每个get()创建'新鲜'对象,除非它们在会话中)。

至于你的恒星问题,当两个人从DB获取同一行时,DB'ACID'属性会确保每个操作都是原子的,所以如果你从Thread中的忍者中移除一个星形t1和提交,线程t2将读取提交的t1值。

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

2

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

因为我对JPA没有多少经验,所以我只能说休眠。 你调用set的对象并不总是你调用get的同一个对象。当Hibernate为你加载对象时,它调用set *方法,然后你(和你的线程)调用剩下的时间。当你(即你的作者线程)修改一个已存在的对象并再次保存它时,你需要保护对该对象的访问(以便其他读写器线程不读取脏数据) 。

+0

所以你期待在后面的例子中看到实体类中的同步?或者您是否期望在应用程序协调的更高级别上看到同步? – 2008-10-08 18:00:57

0

JPA/Hibernate实体是POJO。 Hibernate和任何JPA提供程序不会更改运行时语义。

所以,如果你有一个简单的POJO的并发问题,你也将它们与你的实体!

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

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

0

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

从Java持久性API(JPA)规范(第3.4.1):

乐观锁是 用于技术确保只有当 没有干预事务已经更新 实体状态的数据( ,因为 实体状态被读取)时才对对应于实体的 状态的数据库数据进行更新。此 可确保更新或删除 数据与数据库的当前状态一致,并且不会丢失干预更新。

您需要将@Version注释添加到您的类和DB表中的一列。

3

在我看来,你永远不应该在线程间共享域对象。事实上,我通常在线程之间共享很少的数据,因为这些数据必须得到保护我已经建立了一些大型/高性能系统,并且从来不必违反这一规则。如果您需要并行工作,那么请这样做,但不要通过共享域对象的实例。每个线程都应该从数据库读取数据,通过变异/创建对象对其进行操作,然后提交/回滚事务。工作进出通常应该是有价值的/只读的对象。 “