2014-03-12 29 views
0

我在我的webapp中遇到了一種情況,我還沒有找到一個好的解決方案。在我的應用中,用戶可以針對外部(對我的應用)音樂數據庫進行搜索。我代表他們進行搜索,然後展示結果。如何在Spring/Hibernate的webapp中允許部分事務失敗?

然後他們可以選擇將一個或多個結果添加到其用戶記錄中。在內部,我在歌曲的表格中創建了一行(如果它不在我的數據庫中),請複製歌曲信息,然後將其添加到收藏中。

問題是兩個用戶都可能試圖將同一首歌曲添加到數據庫(它具有唯一性約束)。一個用戶會成功,另一個用戶會失敗,但直到整個交易結束。如果用戶A獲勝並且用戶B不獲勝,我不希望將該歌曲添加到數據庫失敗;我想要捕獲該錯誤,從我的數據庫中加載(現在存在的)歌曲,然後繼續將其添加到用戶集合中。

從我在Hibernate文檔中閱讀的內容(這裏是:http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/transactions.html#transactions-basics-uow),這不是它打算如何使用。當然,我可以讓整體交易失敗,並將其報告給用戶,但對於應該可恢復的情況來說,這是一種糟糕的用戶體驗。

有沒有一種非哈克的方式來處理這個問題?謝謝!

+0

你問關於併發性還是關於數據庫約束? – Dan

回答

0

你可以同步這些交易嗎?看看併發性,因爲我認爲這是你正在尋找的。

0

先生,你可以在其中使用監視器和信號量的概念。考慮將其保存爲關鍵區域,並通過互斥體傳遞這兩個線程,以便只有其他人可以等待訪問。 一旦其他人可以進入關鍵區域。

0

有兩種方法,通過將業務方法的隔離級別提高到SERIALIZABLE來防止錯誤,檢查歌曲是否先存在,然後才插入。

沒有其他隔離級別就足夠了,因爲在這種情況下我們需要防止幻像讀取。

否則可以使用樂觀鎖定。 Spring會將Hibernate異常轉換爲彈簧OptimisticLockingFailureException

這可以被夾在調用業務方法的方法:

@Service 
public class SongService { 
    @Transactional 
    public void addSongIfNotExists() { 
     ... 
    } 
} 


@Service 
public class AddSongFailureHandlingService { 

    @Autowired 
    private SongService songService; 

    // NEVER makes sure no transaction is ongoing, that would prevent commit after 
    // calling addSongIfNotExists 
    @Transactional(propagation= Propagation.NEVER) 
    public callingMethod() { 

     try { 
      // no transaction is propagated to this method, this will cause a 
      // commit attempt 
      songService.addSongIfNotExists(); 

     } catch(OptimisticLockingFailureException exc) { 
      // transaction rolled back, song already inserted 
      // apply recovery logic 
      ... 
     } 
    } 
} 

要知道,一個事務回滾之後,任何實體管理器/ Hibernate的Session打開需要丟棄這一點很重要。

因此,如果您需要在數據庫中執行某些操作以恢復樂觀鎖定異常,請務必調用另一個@Transactional業務方法,以便獲得新的會話/實體管理器。

相關問題