2016-11-11 115 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