2016-11-11 63 views
20

我使用春数据CrudRepository和悲观锁

  • 春季启动1.4.2
  • 春数据JPA 1.10.5
  • 的PostgreSQL数据库9.5

我想有一个findOne方法悲观锁定在我的Spring数据存储库中,它与已经提供的findOne方法分开。

this answer我写道:

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

这几乎工程。

不幸的是,这不会刷新实体管理器缓存中的实体的先前实例。我有更新我的注册状态两个并发请求

  • 的第一个交易的第二个等待提交
  • 第二个不没有考虑到第一个所做的更改。

因此破坏行为。

任何线索为什么@Lock没有开箱刷新实体管理器?

更新

这里是请求的示例代码:

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

你必须向我们展示了代码,它改变了实体值。为什么你在只读实体的方法上用'PESSIMISTIC_WRITE'锁定表? –

+0

我在注释为“@ Transactional”的方法中使用代码,在那里读取实体,更新它并写回。相当标准。我想避免这个操作的并发性,所以我想使用一个悲观的锁。我只想在'update'之前执行'select for update'。 – rcomblen

+0

整个代码块是事务性的,因此使用相同的entitymanager。 'EntityManager'充当第一级缓存。您首先检索没有锁的对象,然后用锁再次检索它。但是,对于第一级缓存,您将检索该对象而不是新的数据库对象。这基本上是'EntityManager'的工作原理,如果你不想让你首先必须“清除”实体管理器。或者说,你为什么首先检索它没有锁在同一TX(这是奇怪的恕我直言)。 –

回答

5

您需要使用entityManger transactionSpring为你创建:

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

} 
+4

如果我使用'entityManager.refresh',它可以正常工作。我只希望我不需要那样。该代码现在正在生产中正常运行。 – rcomblen