简体   繁体   中英

How to handle JPA OneToMany relations in a multi-threaded environment

I'm having two JPA entities which are linked to each other. A person can be member of zero or one groups. A group can have zero or more members.

@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;

}

The problem I'm having is that multiple threads can remove/add persons to a group. So when two threads remove a person from the same group, each of them is unaware of what happened in the other thread.

  1. personA and personB belong to group1
  2. Thread1 removes personA from group1
  3. Thread2 removes personB from group1
  4. Thread1 does some post-processing with personB
  5. Thread2 does some post-processing with personA

I know I can wrap this all in a synchronized block, but that would not help me if the application is running on multiple machines that share a database.

How could JPA be aware that the collection on GroupEntity has been changed by another thread?

You are right about the synchronized block: that will not apply across JVMs

Use a database locking strategy to handle concurrent updates to an entity

Instead, you want to consider a database locking strategy and using database transactions. JPA supports both optimistic and pessimistic locking . You can typically achieve better performance with optimistic locking which uses a version field to track updates to each of your entities.

In your scenario, if the threads each perform their remove and update in operations in one database with optimistic locking, one thread will succeed and the other will throw a locking exception because it tried to update an entity which has been removed

Use a join table to eliminate contention between modifications to Group membership and Person attributes

Assuming that removing the Person from the group does not delete the person, there may be another way to remove contention in this scenario. Typically, in a one-to-many relationship, the Person row will hold a reference to the group ID to which it belongs. This causes Thread 1 and Thread 2 to compete for updates to a Person row. Instead, you could move the one-to-many relationship to a separate join table so that Thread 1 can remove PersonA from Group1 at the same time that Thread2 does post processing with PersonA

On synchronizing relationship collections between JVMs

JPA implements several persistence patterns Martin Fowler details in Patterns of Enterprise Application Architecture . One of these patterns is the Unit of Work which tracks changes made during a "session". I believe Thread1 and Thread2 are working in separate units of work and therefore they should avoid trying to synchronize their in-memory caches of data retrieved from the database. Instead, Thread1 will get Thread2's changes the next time it queries the database. Let the database own the concern of synchronizing state across the distributed system.

If you need to manage distributed transactions, look into JTA ; however, I'm not sure which JPA providers are able to distribute their unit of work's in-memory state within a distributed transaction. This is situation I would try to avoid instead of handle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM