java - 如何在多线程环境下处理JPA OneToMany关系

标签 java multithreading jpa concurrency

我有两个相互链接的 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;

}

我遇到的问题是多个线程可以将人员删除/添加到组中。因此,当两个线程从同一组中删除一个人时,每个线程都不知道另一个线程中发生了什么。

  1. personA 和 personB 属于组 1
  2. 线程 1 将 personA 从组 1 中删除
  3. 线程 2 将 personB 从组 1 中删除
  4. Thread1 对 personB 进行一些后处理
  5. 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/

相关文章:

java - @OneToMany关系: "one" id not persisted

java - 将对象转换为java中的接口(interface)?

java - 当新值不正确时重置 JTable 单元格中的旧值

java - Spring MVC 一个表单将数据提供给另一个表单

c# - .NET 中的多层应用程序

Java Bean - 通过原型(prototype)范围需要相同的对象

java - 不需要的 block 线程导致 Tomcat 失败

java - ejb j2ee 中的持久上下文范围和其他问题

eclipse - Persistence.xml 放在 eclipse 项目的什么位置

java - java中的类型转换规则