2012-04-16 54 views
4

使用Hibernate 4.0我有三個休眠實體:重複使用Hibernate的關鍵問題

歌,封面作品,CoverImage

歌代表的音樂文件,CoverImage代表圖像和封面作品用於涉及CoverImages歌曲,一歌曲可以包含多個封面圖片。

Song和CoverArt有一個由Hibernate自動生成的主鍵。但Cover Image主鍵手動完成,構造爲圖像數據的MessageDigest。我這樣做是因爲相同的圖像可以被許多歌曲使用,我不希望在數據庫中多次存儲同一圖像的單獨實例,這也是因爲可以從數據庫中構建密鑰,我可以在數據庫中檢查文件是否已經存在存在,如果是的話,檢索它而不是構建一個新的CoverImage。

問題是我的應用程序是多線程的,Hibernate實際上並沒有將事情直接提交給數據庫,所以線程1可能會檢查coverimage是否已經在數據庫中,發現它並沒有構建新的歌曲,CoverArt和CoverImage對象。但是,數據被提交到CoverImage可能是由一個單獨的線程加入數據庫的時間,所以我得到一個例外,因爲我的新CoverImage使用具有相同的密鑰 作爲現有

session.merge(coverImage); 

所以我認爲會處理這個,但它似乎沒有幫助

+0

添加更多代碼片段。 – 2012-04-16 11:40:24

回答

4

沒有可靠的方式來處理這種情況,除了重試失敗的事務。

因此,如果由於主鍵CoverImage上的約束違規而導致事務回滾,則應重試該事務,假定CoverImage已存在。請注意,你需要一個新的Session來做到這一點,因爲Hibernate異常是不可恢復的。

merge()無法處理此問題,因爲它的原因更深入,在事務隔離語義。在基於MVCC的現代DBMS中,每個事務都會看到它自己的數據庫快照。因此,併發事務可以對它們的快照進行衝突性更改(儘管它們不能對同一記錄進行更改,因此這些更改必須是不相交的),並且這種衝突只能在提交期間由DBMS檢測到,並且只有在導致約束違反,如你的情況(沒有約束衝突將被忽視,見write skew anomaly)。

由於merge()在事務內部工作,所以無法看到其他事務在其快照中做了什麼,因此無法解決此問題。

+1

+1。請注意,您可以使用非常短的事務來獲取或創建CoverImage,然後重試此短事務,而不是重試整個全局事務。唯一的缺點是您可以成功創建CoverImage並使全局事務失敗,這將導致CoverImage未鏈接到任何CoverArt,但我不認爲這會是一個問題。 – 2012-04-16 13:19:51

+0

是的我認爲創建單獨的txn只是爲了添加封面圖像可能是最有意義的,但我不明白爲什麼調用merge()不處理問題 – 2012-04-16 14:39:54

+0

@ijabz:已更新。 – axtavt 2012-04-16 15:00:52

1

另一種選擇是使用wrapper for coverImages。例如。 CoverImageWrapper。 CoverImageWrapper除了messageDigest以外,還有其自己的基於uuId的密鑰。這個類與CoverImage一對一連接。

在存儲到數據庫中時,這個CoverImageWrapper鍵總是由應用程序生成的,所以這樣你就擁有了由應用程序生成的所有三個鍵(Song,CoverArt和CoverImageWrapper),它在所有線程中都是唯一的。所以,這樣你可以避免重複鍵的異常。

+0

但是,你最終會得到多個封面圖像行,爲同一個圖像不會嗎? – 2012-04-16 15:31:35

+0

我想,你可以在CoverImageWrapper和CoverImage類之間建立多對一的映射關係。 Hibernate會在存儲這樣的協議時保持謹慎。那裏不會重複CoverImage實例。您需要在coverImage類中覆蓋equals和hashcode。 – zaga250 2012-04-16 15:40:16

+0

好吧,但我真的想讓事情變得簡單,這對我來說太複雜了,謝謝。 – 2012-04-16 15:56:38