關於在使用hibernate在提交事務時常遇到的異常:java
an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session數據庫
其實這個異常通常都是和咱們在操做session flush方法和提交事務過程當中會拋出的,下面就具體結合session的事務和聲明週期來具體分析下,爲何會有這樣的異常;緩存
首先來看下,session的生命週期session
Hibernate中java對象的三種狀態:ui
一、臨時狀態(transient):用new語句建立,尚未被持久化,不處於Session的緩存中。 this
二、持久化狀態(persistent):已使用save()或者saveOrUpdate()方法,處於Session的緩存中和數據庫表中,生成了本身的Oid標識。 hibernate
三、遊離狀態(detached):被持久化,已使用evict(Object),session.close()或者使用clear()清除緩存,再也不處於Session的緩存中或不存在數據庫表中,可是依然是存在本身的OId標識。 對象
對象的狀態轉換blog
從上面的圖中咱們能夠很清楚的明白一個java對象在session中三種狀態的轉換,生命週期
而後在來看看session緩存在何時會被清除:
1.當應用程序調用org.hibernate.Transaction的commit()方法的時候,commit()方法先清理緩存,而後再向數據庫提交事務。
2.當應用程序顯式調用Session的flush()方法的時候,其實這個方法咱們幾乎不多用到,由於咱們通常都是在完成一個事務纔去清理緩存,提交數據更改,這樣咱們直接提交事務就能夠。
clear()和evict(Object)的區別:
從參數就能夠看出,clear()是會清除整個session中的緩存,evict(Object)是將一個對象從session緩存中清除;
其實在session持久化操做和數據庫中之間還有一層對象緩衝區(entityEntries)
Commit():此方法在執行後會更新對象在對象緩存區中的existsInDatabase=true;
Flush():會按save,update,delete順序執行,把緩存中的數據flush入數據庫中,並清空緩存區;
下面幾個例子能夠充分說明咱們異常拋出的狀況:
SessionFactory sf = new Configuration().configure().buildSessionFactory() ;
Session s = sf.openSession();
Person person = new Person();
Transaction tran = s.beginTransaction(); (1)
s.save(person); (2)(此處一樣能夠爲update delete)
s.evict(person); (3)
tran.commit(); (4)
s.close();(5)
看上面的代碼,再參照下咱們的示例圖和commit()方法,就能夠很明顯的發現代碼問題的所在,在第四步evict()方法將cat對象從對象緩存區清除,當咱們執行commit()方法後,更新對象在緩存區中狀態的時候,因爲已被清除,就會出現上述斷言的異常;
Person person1 = new Person ();
person1.setName(「tom」);
s.save(person1);
person1.setName(「mary」);
s.update(person1);
Person person2 = new Person ();
person2.setName(「tom」);
s.save(person2);
s.flush();
其實在這裏咱們看這個代碼的時候感受是沒問題 ,在這裏咱們能夠參考下剛提到的flush()方法,此方法會按save,update,delete的順序進行提交事務,因此在這裏會拋出主鍵衝突的異常,解決的辦法是在update()操做後面也加入flush();
總的來講,因爲flush()的特殊處理機制,雖然不建議使用此方法,可是在一些複雜的事務處理過程當中,加入此方法雖然會破壞事務的一個提交的完整性,可是能夠規避一些不可預見的異常狀況!