2011-11-14 39 views
26

這是一個設計問題,未提交具體代碼以保護我的底部。org.hibernate.Session.clear()認爲是有害的?

當使用Hibernate的標準工作流程如下:

  1. 打開會話
  2. 開始事務
  3. 做生意(讀取和修改數據)
  4. 提交事務
  5. 關閉會話

有可能迭代通過2-4。

Session.clear()的合理用例是什麼?

答:我具體的問題是一個加載和修改實體的代碼段,然後clear()會話,實質上是拋棄了所做的更改。 (要完成的業務任務不包括修改實體,因此代碼「有效」)。

對我來說,合適的設計應該是確保(大)一段代碼不會更改它不想保存的內容? B:爲了方便/靈活性,我想Session.clear()存在,不是因爲它是一個好主意,所以不要使用它。

我誤解了Hibernate的哲學嗎?

C:Subquestion:框架代碼在任務完成時無條件地清除()會話是不是一個好主意?恕我直言,框架應該抱怨,如果任務完成時會話很髒!該會話應該關閉,看到任務完成...(不考慮性能的分鐘)

(標籤A,B和C,所以你可以指出你正在回答的部分)。

回答

29

廣告。 A:看起來你知道clear()做什麼。明確調用它的原因是從L1緩存中刪除所有受管實體,以便在一個事務中處理大型數據集時不會無限增長。

它放棄對託管實體進行的所有更改未明確保留。這意味着您可以安全地修改實體,明確更新並清除會話。這是正確的設計。顯然,如果沒有更改(長,但只讀會話),clear()總是安全的。您可以使用stateless sessions

Ad。 B:不,它存在以上原因:確保L1(會話緩存)不會增長太多。當然,手動維護是一個糟糕的主意,並且表明另一種工具應該用於大型數據集,但有時這是必須的。

請注意,在JPA規範中還有clear()flush()方法。在這種情況下,在致電clear()之前,您應該始終先致電flush()將更改推送到數據庫(顯式更新)。

Ad。 C:當他/她用骯髒的更改清除會話時,警告用戶(可能通過發出警告消息而不是拋出異常)實際上是一個好主意。另外我不認爲框架代碼應無條件地調用clear(),除非它確定用戶代碼運行刷新或不作任何更改。

+0

ad A):在一個事務中處理大型數據集聽起來像是糟糕的設計給我的(理所當然,我可能沒有想到所有可能的問題......)。 「明確」堅持一個實體是什麼意思? session.save(實體)? Ad C):聽起來我們同意框架不應該這樣做 - 框架不可能確定客戶端做了什麼。 :-) –

+0

@MortenLauritsenKhodabocus:顯然,在一個Hibernate事務中加載和修改數千條記錄是一個標誌,應該使用不同的工具。是的,通過*顯式*我的意思是調用'save()'而不是讓Hibernate發現髒實體。 –

+0

@MortenLauritsenKhodabocus請賜教。爲什麼在一個事務中處理一個大型的只讀數據集是一個壞主意?在我看來,如果它涉及在數據庫方面訂購,它會很聰明,因爲它可以節省多次重新訂購。不使用事務意味着數據庫將記住您作爲同一事務的不同查詢的訂購狀態?另外,Hibernate's ScrollableResults API的功能不是「使用一個事務」嗎? – KyleM

3

下面是我剛碰到的另一個原因:在同一事務中多次調用存儲過程時緩存以前的結果。簡化代碼如下。

//Begin transaction 
SessionFactory sf = HibernateSessionFactory.getFactory(); 
Session dbSession = sf.getCurrentSession(); 
dbSession.beginTransaction(); 

//First call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "A"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Second call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "B"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Commit both  
dbSession.getTransaction().commit(); 

第一次調用後沒有clear(),第一次調用的結果集行被複制到第二次調用的結果集中。我正在使用Oracle 11gR2。

複製此錯誤的關鍵是在同一個事務中進行兩個調用。由於我在視圖模式中使用了開放會話,因此兩個調用都會在同一個事務中自動發生(因爲原始代碼在循環中調用每個結果的循環)。因此我把它稱爲一個bug。否則可能被認爲是一個功能,但即使這樣clear()也不會在代碼示例中調用,並聲明它應該被調用。 session.flush()什麼都沒做。 映射文件如下。因此,我已將clear()添加到所有過程調用的末尾。尚未用我的自定義SQL調用進行測試。這是微不足道的東西;驚訝存在的錯誤。

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping> 
    <class name="com.jfx.rr.model.ShipSummaryRow"> 
     <id name="id" type="integer"/> 
     <property name="shipQtrString" not-null="true" type="string"/> 
     <property name="shipAmount" not-null="true" type="double"/> 
    </class> 
    <sql-query callable="true" name="RR_CUST_OPP_DATA"> 
     <return class="com.jfx.rr.model.ShipSummaryRow"> 
      <return-property column="SHIPPED_ID" name="id"/> 
      <return-property column="SHIP_QTR" name="shipQtrString"/> 
      <return-property column="SHIPPED_AMOUNT" name="shipAmount"/> 
     </return> 
     { call RR_DASHBOARD_REPORTS_PKG.RR_CUST_OPP_DATA(?, :custName) } 
    </sql-query> 
</hibernate-mapping> 
相關問題