ikorennoy
ikorennoy

Reputation: 230

How to protect from concurrent entity creation in Spring JPA

I have the following problem

My service, which is the link between the Rest controller and JPA Repository performs some checks before creating the entity in the database. But the following problem arose, if client 1 and client 2 have a common logical parent and at the same time send a request to create an entity, that is, it is likely that they will simultaneously pass checks and be able to create an entity that in theory should not be created, how to avoid this problem? Moreover, if customers have a different parent, then they can create these entities in any situation.

There is one idea how this problem can be solved, before creating an entity it is necessary to acquire a lock on the parent line before creating the entity, then the second client will get an error when trying to create the entity, but how to implement this approach in Spring JPA? Thank.

For a better understanding, I will give an example:

public class Parent {
    @Id
    Long id;
    @OneToMany
    @JoinColumn(name = "parent_id")
    private List<Child> childs;
}

All clients who request entity creation have some parent The approximate code of the service and method that creates something:

public SomeEntity createSomeEntity(SomeEntity someEntity) {
    // further, some checks are made on these lines
    // checks
    if (checks are passed) {
        someEntityRepository.save(someEntity);
    } else {
        // entity will not be created in the database
    }
}

if clients have different parents, then there is no problem, but if they have one parent, then it is hypothetically possible that two clients pass checks at the same time and create entities that conflict.

At the same time, I don’t want to make this code synchronous or to create SomeEntity every time using Pessimistic Locking since then multithreading is lost for clients with different ancestors. As I said, I had the idea that this problem can be solved if, before checking in the service class acquire the lock on the parent line, then we will not lose parallelism and the situation where clients with a common parent create an entity that should not be excluded, but how to do it with Spring JPA I don’t know

Used PostgreSQL Database version 12.2

If I were to do this with SQL code, in this place, we make the synchronous addition of the SomeEntity entity to the client, whose parent has id = 1:

START TRANSACTION;
SELECT * FROM parent WHERE id = 1 FOR UPDATE;
-- here is the logic for adding SomeEntity 
COMMIT; 

Upvotes: 2

Views: 946

Answers (1)

Ken Chan
Ken Chan

Reputation: 90457

Enable optimistic locking on the parent , and lock the parent using OPTIMISTIC_FORCE_INCREMENT locking mode when you start to create children for that parent.

It will ensure that if multiple threads are creating children for the same parent at the same time , only one thread will succeed and others will fail . For the fail one , just catch the OptimisticLockException and retry again.

Please note if another thread is trying to update the content of the parent at the same time , it will also considered as conflict . So technically speaking for your use case , it will ensure the only one thread will create children for the same parent , or modifying the same parent.

Code wise it look like:

@Entity
public class Parent {


  @Version
  private long version;

}

And the code to lock the parent :

Parent parent = entityManager.find(Parent.class, 1L);
entityManager.lock(parent, LockModeType.OPTIMISTIC_FORCE_INCREMENT);

// And use this parent to create the children as usual

Upvotes: 2

Related Questions