Monday, June 5, 2017

Spring data CrudRepository and pessimistic lock

Leave a Comment

I'm using

  • Spring Boot 1.4.2
  • Spring Data JPA 1.10.5
  • PostgreSQL 9.5 database

I want to have a findOne method with pessimistic lock in my Spring Data repository that is separate from the findOne method that is already provided.

Following this answer I wrote:

public interface RegistrationRepository extends CrudRepository<Registration, Long> {     @Lock(LockModeType.PESSIMISTIC_WRITE)     @Query("select r from Registration r where r.id = ?1")     Registration findOnePessimistic(Long id); } 

This almost works.

Unfortunately, this does not refresh previous instance of my entity in the entity manager cache. I have two concurrent requests updating the status of my registration

  • the second one waits for the transaction of the first one to commit
  • the second one does not take into account the changes made by the first one.

Hence broken behavior.

Any clue why @Lock does not out of the box refresh the entity manager?

Update

Here is the requested example code:

public interface RegistrationRepository extends CrudRepository<Registration, Long> {      @Lock(LockModeType.PESSIMISTIC_WRITE)     @Query("select r from registration_table r where r.id = ?1")     Registration findOnePessimistic(Long id);  }  public void RegistrationService {      @Transactional     public void doSomething(long id){         // Both threads read the same version of the data          Registration registrationQueriedTheFirstTime = registrationRepository.findOne(id);          // First thread gets the lock, second thread waits for the first thread to have committed         Registration registration = registrationRepository.findOnePessimistic(id);         // I need this to have this statement, otherwise, registration.getStatus() contains the value not yet updated by the first thread         entityManager.refresh(registration);          registration.setStatus(newStatus);         registrationRepository.save(registration);     } } 

1 Answers

Answers 1

You need to use the entityManger transaction that Spring creates for you :

    @Transactional     public void doSomething(long id){         // Both threads read the same version of the data          Registration registrationQueriedTheFirstTime = registrationRepository.findOne(id);          // First thread gets the lock, second thread waits for the first thread to have committed         Registration registration = registrationRepository.findOnePessimistic(id);         // I need this to have this statement, otherwise, registration.getStatus() contains the value not yet updated by the first thread         entityManager.refresh(registration);          EntityManager em = EntityManagerFactoryUtils.getTransactionalEntityManager(<Your entity manager factory>);         em.refresh(registration);         registration.setStatus(newStatus);         registrationRepository.save(registration);     }  } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment