我有两个相互链接的 JPA 实体。 一个人可以是零个或一个群体的成员。 一个组可以有零个或多个成员。
@Entity
public class PersonEntity {
@ManyToOne
@JoinColumn(name = "GROUP_ID")
private GroupEntity group;
}
@Entity
public class GroupEntity {
@OneToMany(mappedBy = "group", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
private Collection<PersonEntity> persons;
}
我遇到的问题是多个线程可以将人员删除/添加到组中。因此,当两个线程从同一组中删除一个人时,每个线程都不知道另一个线程中发生了什么。
- personA 和 personB 属于组 1
- 线程 1 将 personA 从组 1 中删除
- 线程 2 将 personB 从组 1 中删除
- Thread1 对 personB 进行一些后处理
- Thread2 对 personA 进行一些后处理
我知道我可以将这一切包装在同步块(synchronized block)中,但如果应用程序在共享数据库的多台计算机上运行,这对我没有帮助。
JPA 如何知道 GroupEntity 上的集合已被另一个线程更改?
最佳答案
你对同步块(synchronized block)的看法是正确的:这不适用于跨 JVM
使用数据库锁定策略来处理实体的并发更新
相反,您需要考虑数据库锁定策略并使用数据库事务。 JPA 支持 optimistic and pessimistic locking 。您通常可以通过乐观锁定获得更好的性能,乐观锁定使用版本字段来跟踪每个实体的更新。
在您的场景中,如果每个线程在一个具有乐观锁定的数据库中执行删除和更新操作,则一个线程将成功,另一个线程将抛出锁定异常,因为它试图更新已被删除的实体
使用联接表消除组成员资格和人员属性修改之间的争用
假设从组中删除该人员并不会删除该人员,则在这种情况下可能有另一种方法来消除争用。通常,在一对多关系中,人员行将保存对其所属组 ID 的引用。这会导致线程 1 和线程 2 竞争对 Person 行的更新。相反,您可以将一对多关系移至单独的联接表,以便线程 1 可以在线程 2 对 PersonA 进行后处理的同时从 Group1 中删除 PersonA
关于同步 JVM 之间的关系集合
JPA 实现了几种持久化模式 Martin Fowler 在 Patterns of Enterprise Application Architecture 中详细介绍了这一点。这些模式之一是 Unit of Work它跟踪“ session ”期间所做的更改。我相信 Thread1 和 Thread2 在不同的工作单元中工作,因此它们应该避免尝试同步从数据库检索的数据的内存缓存。相反,Thread1 将在下次查询数据库时获取 Thread2 的更改。让数据库负责跨分布式系统同步状态。
如果您需要管理分布式事务,请查看 JTA ;但是,我不确定哪些 JPA 提供者能够在分布式事务中分发其工作单元的内存中状态。这是我会尽力避免而不是处理的情况
关于java - 如何在多线程环境下处理JPA OneToMany关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38878319/