似乎我见过的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; }
}
投掷之星方法需要同步吗?我肯定不希望我的忍者失去任何投掷的星星。
答案 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注释,在数据库表中添加一列。