7

我的項目使用hibernate與spring事務管理器和我的數據庫是postgres(可能無關緊要)。處理數據庫約束hibernate

我正在嘗試讀取大的XML文件,並構造出那些對象(對象不是很大,但金額是),並將它們插入到數據庫中。

如果有機會,我的某個對象違反了數據庫約束,整個過程就會停止。我怎樣才能跳過那些違反數據庫約束的?或者將他們的ID或其他日誌記錄到日誌文件中?

問題更新:

我一直很瀏覽槽和發現,對於批量插入它的最好建議使用無狀態會話,但我仍然得到同樣的問題,插入停止:

May 26, 2012 4:45:47 PM org.hibernate.util.JDBCExceptionReporter logExceptions 
SEVERE: ERROR: duplicate key value violates unique constraint "UN_FK" 
    Detail: Key (fid)=(H1) already exists. 

下面是我的代碼的相關部分,用於解析xml並插入到db中,爲了簡單起見,我們假設我正在插入電影:

//class field 
@Autowired 
private SessionFactory sessionFactory; 

@Override 
public void startDocument() throws SAXException { 
    session = sessionFactory.getCurrentSession(); 
} 

@Override 
public void endElement(String uri, String localName, String qName) throws SAXException { 
if (qName.equalsIgnoreCase("FILM")) { 
     movie.setCategory(category); 
     movie.setAdded(new Date()); 
     session.insert(movie); 
    } 
} 

我將這個屬性設置爲app-ctx hibernate.jdbc.batch_size爲100.爲了避免這種情況,在插入之前確實需要做select?

更新2:

如果我使用StatelessSession而不是會議上,我得到20個以防萬一刀片和比無限期地處理停止無任何異常或任何東西。

我認爲數字20是因爲我與tomcat共享連接並且有maxActive="20"

賞金更新:

我真的很想看到有人提供的解決方案(不選擇防守如果可能的話)。使用無狀態會話或只是會話。

回答

4

大多數類型的約束條件,例如,如果某列可以爲空或具有最大寬度,則可以使用Hibernate Validator進行檢查。在嘗試持久化之前,只需手動對對象執行驗證即可。

對於某些事情,特別是獨特的約束,您需要執行'defensive'select來查看是否存在衝突,或者維護一組已插入的內存值。

2

要從xml文件插入大量對象,應該考慮使用spring批處理。 參數skip-limit允許您在停止批處理之前,告知xml中有多少錯誤行。 也檢查skip-policy和skippable-exception;參見Spring文檔中的參考文獻Configuring a Step

如果你不想使用spring批處理,只需使用一個簡單的try catch,它將允許你的進程繼續下去直到結束。

+0

一個簡單的try catch語句不會做。一旦Hibernate拋出異常,會話狀態就不一致,必須回滾事務,並關閉會話。而且,只有在錯誤記錄持續存在很久之後,異常纔會在刷新時被拋出。 –

+0

這就是爲什麼建議在這種情況下使用無狀態Hibernate會話的原因。它可以防止不連貫的狀態,並減少內存消耗(或者不必驅逐已處理的實體) –

2

你覺得爲什麼這一切都必須是一個大的交易?事實上,你所描述的一切都意味着你實際上在這裏有很多交易。對於「出錯」的對象,只需驅逐該實體並回滾事務。如果「FILM」元素定義了一個對象圖,它會變得更加複雜一些,但這個想法是一樣的。

4

我認爲不可能完全驗證某些東西,以保證插入成功。在某些情況下,不管你做什麼,其他人都可以在驗證和插入之間插入一些東西到數據庫中,導致違反約束。

在大多數情況下,我只是推薦像處理其他任何異常一樣處理異常。

0

雖然這是一個老問題,但我最近面臨類似的情況,這就是我所做的。

我也使用了無狀態會話,但這是我做了不同的事情。我發出了一個session.insert,它由於約束違反而成功或失敗,違反了約束條件違反了ConstraintViolationException,引發它會給你插入失敗的對象,做任何你想對失敗對象所做的事情。除了事務處理還爲每個插入使用了保存點(保存點更便宜並且不會對性能造成太大影響),所以當事務由於某些問題而失敗時,直到最後一個保存點(在catch子句中)才執行提交。

我的代碼看起來是這樣的:

try { 
    session.connection().setSavePoint("lastSaved"); 
    session.insert(obj); 
} 
catch(ConstraintViolationException) { 
    log.error(obj.getUserId()); 
    .... 
} 
tx.commit(); 
.... 
catch(TransactionException e) { 
    tx.rollback(); 
}