Spring data CrudRepository and pessimistic lock

2019-03-22 18:37发布

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条回答
够拽才男人
2楼-- · 2019-03-22 18:47

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);
    }

}
查看更多
登录 后发表回答