2017-10-18 57 views
5

我發現了很多關於此主題的帖子,但所有答案只是鏈接到沒有示例代碼的文檔,即如何在實踐中使用併發。Spring中的Hibernate併發性

我的情況:我有一個實體House與(爲簡單起見)兩個屬性,number(id)和owner。數據庫使用10 Housesnumber 1-10和owner總是null進行初始化。

我想分配一個新的所有者到目前沒有所有者,最小的number。我的代碼如下所示:

@Transactional 
void assignNewOwner(String newOwner) { 
    //this is flagged as @Transactional too 
    House tmp = houseDao.getHouseWithoutOwnerAndSmallestNumber(); 

    tmp.setOwner(newOwner); 

    //this is flagged as @Transactional too 
    houseDao.update(tmp); 
} 

對於我的理解,儘管使用了@Transactional,同樣House可以兩次分配給不同的業主,如果兩個請求獲取相同的空Housetmp。我如何確保這不會發生?

我知道,包括在更新中選擇空的House會解決問題,但是在不久的將來,我想修改/使用更多的tmp對象。

+1

'houseDao.update(tmp);'是不必要的,因爲'tmp'是一個託管實體(您處於事務性方法)。這些更改將被保留,而不需要單獨的update()。 – Kayaman

回答

1

樂觀

如果添加版本列到你的實體/表,那麼你可以利用一種叫做樂觀鎖機制的。這是確保實體的狀態自從我們在事務環境中獲得它以來沒有改變的最有效方法。使用session你可以調用setLockMode(LockModeType.OPTIMISTIC);

然後,該交易將提交之前,持久性提供者將查詢該實體的最新版本,並檢查它是否已經被其他事務遞增

一旦你createQuery 。如果是這樣,你會得到一個OptimisticLockException和一個事務回滾。

悲觀

如果不這樣做版本的行,那麼你就留下了悲觀洛這基本上意味着你phycically創建在數據庫級別上查詢實體和其他事務的鎖無法讀取/更新這些某些行。

您實現由Query對象上設置這樣的:

setLockMode(LockModeType.PESSIMISTIC_READ);

setLockMode(LockModeType.PESSIMISTIC_WRITE);

1

其實這是很容易 - 至少在我看來,我要抽象當你說Pessimistic/Optimistic時,會產生什麼Hibernate。您可能會認爲這是SELECT FOR UPDATE - 但情況並非總是如此,MSSQL AFAIK沒有這種功能...

這些是JPA註釋,它們保證一些功能,而不是實現。

基本上它們是完全不同的東西 - PESSIMISTIC vs OPTIMISTIC鎖定。當你做一個悲觀的鎖定時,你至少在邏輯上做一個synchronized block--你可以做任何你想做的事情,並且你在交易的範圍內是安全的。現在,無論是否爲row, table or even page保留鎖都未指定;所以有點危險。通常數據庫可能會使鎖升級,如果我重新調用正確,MSSQL會這樣做。

明顯鎖定飢餓是一個問題,所以你可能會認爲鎖定會有所幫助。作爲一個方面說明,這是什麼transactional memory is in modern CPU;他們使用相同的思維過程。

因此,樂觀鎖定就像說 - 我會用ID /日期等標記這行,然後我會拍攝一個快照並使用它 - 在提交之前我會檢查該Id是否有更改。很顯然,在這個數據上有爭用,但不是數據。如果它已經改變 - 中止(又名投擲OptimisticLockException)否則提交工作。

困擾所有人IMO的事情是OptimisticLockException - 你是如何從中恢復的?這裏是你不會喜歡的東西 - 它取決於。有些應用程序只需簡單的重試就足夠了,有些應用程序在這種情況下是不可能的。我在罕見的場景中使用過它。

我通常會用Pessimistic鎖定(除非Optimistic完全不是一個選項)。同時我會看看hibernate爲那個查詢產生了什麼。例如,您可能需要一個index來獲取條目DB的實際鎖只是行 - 因爲最終這就是你想要的。