如何在多线程环境中处理JPA OneToMany关系

时间:2016-08-10 15:57:27

标签: 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属于group1
  2. Thread1从group1中删除personA
  3. Thread2从group1中删除personB
  4. Thread1使用personB进行一些后处理
  5. Thread2使用personA进行一些后处理
  6. 我知道我可以将这一切包装在一个synchronized块中,但如果应用程序在共享数据库的多台机器上运行,那对我没有帮助。

    JPA如何知道GroupEntity上的集合已被另一个线程更改?

1 个答案:

答案 0 :(得分:1)

您对同步块是正确的:这不适用于JVM

使用数据库锁定策略来处理对实体的并发更新

相反,您需要考虑数据库锁定策略并使用数据库事务。 JPA支持optimistic and pessimistic locking。您通常可以通过乐观锁定获得更好的性能,该锁定使用版本字段来跟踪每个实体的更新。

在您的方案中,如果每个线程都使用乐观锁定在一个数据库中的操作中执行删除和更新,则一个线程将成功,另一个线程将抛出锁定异常,因为它尝试更新已删除的实体

使用联接表消除对组成员资格和人员属性的修改之间的争用

假设从组中删除Person不会删除此人,可能有另一种方法可以删除此方案中的争用。通常,在一对多关系中,Person行将保存对其所属的组ID的引用。这会导致线程1和线程2竞争Person行的更新。相反,您可以将一对多关系移动到单独的连接表,以便线程1可以在Thread2使用PersonA进行处理的同时从Group1中删除PersonA

在JVM之间同步关系集合

JPA在Patterns of Enterprise Application Architecture中实现了几个持久性模式Martin Fowler的详细信息。其中一种模式是Unit of Work,用于跟踪“会话”期间所做的更改。我相信Thread1和Thread2在不同的工作单元中工作,因此他们应该避免尝试同步从数据库中检索的数据的内存中缓存。相反,Thread1将在下次查询数据库时获得Thread2的更改。让数据库拥有跨分布式系统同步状态的关注。

如果您需要管理分布式事务,请查看JTA;但是,我不确定哪个JPA提供商能够在分布式事务中分配其工作单元的内存状态。这是我试图避免的情况,而不是句柄