1. 程式人生 > >Hibernate的clear flush evict 方法詳解

Hibernate的clear flush evict 方法詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

最近看了下session,發現session.flush()方法有明顯的誤人子弟,明顯亂說,也不對,講的不清楚,對此我很氣憤,你講錯了的讓大家都跟著錯了,還到處轉載你的錯誤,你害羞麼,我想問下作者。

特此給糾正下很多錯誤

session.flush()方法的作用其實就是讓session的快取的資料(session就是一級快取)刷入到資料庫裡面去,讓資料庫同步,你可以更簡單的理解就是,強制讓session的資料和資料庫的資料同步,而不是什麼清除快取,我就奇怪了,清除快取明明是session.clear()方法,在使用flush方法一般之前都是對一個物件進行CRUD的操作,然後你呼叫flush方法,就及時的同步到資料庫裡面去,其實session.flush()方法用的最好的一塊是在處理大量資料的時候我們可以控制數量,比如,我們要儲存1萬個物件,我們可以這樣做

if(i%20==0){

session.flush();//強制同步資料到資料庫裡面去

session.clear();清除快取

}

這樣提高工作效能。



1.Clear 方法

      無論是Load 還是 Get 都會首先查詢快取(一級快取) 如果沒有,才會去資料庫查詢,呼叫Clear() 方法,可以強制清除Session快取。

例:

[c-sharp]  view plain copy
print
?
  1. public void testClear(){  
  2.         Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
  3.         session.beginTransaction();  
  4.         Teacher t = (Teacher) session.get(Teacher.class, 3);  
  5.         System.out.println(t.getName());  
  6.         Teacher t2 = (Teacher) session.get(Teacher.class, 3);  
  7.         System.out.println(t2.getName());  
  8.         session.getTransaction().commit();  
  9.     }  

這裡雖然用了2  get 方法( get 方法會立即執行 sql 語句),但因為第一次執行了會快取一個 ID  3 的實體,所以雖然有 2  get 方法只執行一次 SQL 語句。

 

[c-sharp]  view plain copy print ?
  1. public void testClear(){  
  2.         Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
  3.         session.beginTransaction();  
  4.         Teacher t = (Teacher) session.get(Teacher.class, 3);  
  5.         System.out.println(t.getName());  
  6.         session.clear();//這裡不clear只會執行一次sql語句,有clear會執行2次  
  7.         Teacher t2 = (Teacher) session.get(Teacher.class, 3);  
  8.         System.out.println(t2.getName());  
  9.         session.getTransaction().commit();  
  10.     }  

這裡在第2  get 前執行 session.clear(), 我們把 hibernate show_sql  出來,它就會執行 2  sql 語句了。 所以session.clear() 會清除快取。

 

2.Flush方法

      可以強制進行從記憶體到資料庫的同步。

例:

[c-sharp]  view plain copy print ?
  1. @Test  
  2.     /** 
  3.      * flush 強制與資料庫同步 
  4.      */  
  5.     public void testFlush(){  
  6.         Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
  7.         session.beginTransaction();  
  8.         Teacher t = (Teacher) session.get(Teacher.class, 3);  
  9.         t.setName("yyy");  
  10.    
  11.         t.setName("yyyyy");  
  12.         session.getTransaction().commit();  
  13.     }  

看這段程式碼,我們setName() 2 次, 但程式只會更改資料庫一次,在 commit 時。

 

[c-sharp]  view plain copy print ?
  1. @Test  
  2.     /** 
  3.      * flush 強制與資料庫同步 
  4.      */  
  5.     public void testFlush(){  
  6.         Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
  7.         session.beginTransaction();  
  8.         Teacher t = (Teacher) session.get(Teacher.class, 3);  
  9.         t.setName("yyy");  
  10.         session.flush();//有flush會執行2次UPDAE,沒有會只執行一次  
  11.         t.setName("yyyyy");  
  12.         session.getTransaction().commit();  
  13.     }  

我們在第2  setName ()時 執行 session.flush().

 

再看hibernate  執行的 sql  語句

[c-sharp]  view plain copy print ?
  1. Hibernate:   
  2.     update  
  3.         Teacher   
  4.     set  
  5.         birthday=?,  
  6.         name=?,  
  7.         title=?   
  8.     where  
  9.         id=?  
  10. Hibernate:   
  11.     update  
  12.         Teacher   
  13.     set  
  14.         birthday=?,  
  15.         name=?,  
  16.         title=?   
  17.     where  
  18.         id=?  

執行了2  Update

 

所以看出來flush 方法會強制與資料庫同步。

 

 

 

 

Flush方法是可以設定的,也就是 fulsh 什麼時候執行是可以設定的

 

 在session.beginTransaction 前設定 FlushMode

 

session.setFlushMode(FlushMode.Always|AUTO|COMMIT|NEVER|MANUAL)

 

FlushMode 5 個值可選

Always:任何程式碼都會 Flush 
AUTO:預設方式 – 自動 
Commit:COMMIT 
Never:始終不 
MANUAL:手動方式


1、NEVEL:已經廢棄了,被MANUAL取代了
2 MANUAL:
如果FlushMode是MANUAL或NEVEL,在操作過程中hibernate會將事務設定為readonly,所以在增加、刪除或修改操作過程中會出現如下錯誤
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition;
解決辦法:配置事務,spring會讀取事務中的各種配置來覆蓋hibernate的session中的FlushMode;
3 AUTO
設定成auto之後,當程式進行查詢、提交事務或者呼叫session.flush()的時候,都會使快取和資料庫進行同步,也就是重新整理資料庫
4 COMMIT
提交事務或者session.flush()時,重新整理資料庫;查詢不重新整理
5 ALWAYS:
每次進行查詢、提交事務、session.flush()的時候都會刷資料庫
ALWAYS和AUTO的區別:當hibernate快取中的物件被改動之後,會被標記為髒資料(即與資料庫不同步了)。當 session設定為FlushMode.AUTO時,hibernate在進行查詢的時候會判斷快取中的資料是否為髒資料,是則刷資料庫,不是則不刷,而always是直接重新整理,不進行任何判斷。很顯然auto比always要高效得多。

 

總結:若OpenSessionInViewFilter在getSession的時候,會把獲取回來的session的flush mode 設為FlushMode.NEVER。然後把該sessionFactory繫結到TransactionSynchronizationManager,使request的整個過程都使用同一個session,在請求過後再解除該sessionFactory的繫結,最後closeSessionIfNecessary根據該session是否已和transaction繫結來決定是否關閉session。在這個過程中,若HibernateTemplate 發現自當前session有不是readOnly的transaction,就會獲取到FlushMode.AUTO Session,使方法擁有寫許可權。
也即是,如果有不是readOnly的transaction就可以由Flush.NEVER轉為Flush.AUTO,擁有insert,update,delete操作許可權,如果沒有transaction,並且沒有另外人為地設flush model的話,則doFilter的整個過程都是Flush.NEVER。所以受transaction保護的方法有寫許可權,沒受保護的則沒有。



設定FlushMode  有個好處是可以節省開銷,比如預設 session 只做查詢時,就可以不讓他與資料庫同步了。


session.evict(obj) :會把指定的緩衝物件進行清除。 

  session.clear() :把緩衝區內的全部物件清除,但不包括操作中的物件。 

  Hibernate 執行的順序如下: 
 (1) 生成一個事務的物件,並標記當前的 Session 處於事務狀態(注:此時並未啟動資料庫級事務)。 
 (2) 應用使用 s.save 儲存物件,這個時候 Session 將這個物件放入 entityEntries ,用來標記物件已經和當前的會話建立了關聯,由於應用對物件做了儲存的操作, Session 還要在 insertions 中登記應用的這個插入行為(行為包括:物件引用、物件 id  Session 、持久化處理類)。 
 (3)s.evict 將物件從 s 會話中拆離,這時 s 會從 entityEntries 中將這個物件移出。 
 (4) 事務提交,需要將所有快取 flush 入資料庫, Session 啟動一個事務,並按照 insert,update,……,delete 的順序提交所有之前登記的操作(注意:所有 insert 執行完畢後才會執行 update ,這裡的特殊處理也可能會將你的程式搞得一團糟,如需要控制操作的執行順序,要善於使用flush ),現在物件不在 entityEntries 中,但在執行 insert 的行為時只需要訪問 insertions 就足夠了,所以此時不會有任何的異常。異常出現在插入後通知 Session 該物件已經插入完畢這個步驟上,這個步驟中需要將 entityEntries 中物件的 existsInDatabase 標誌置為 true ,由於物件並不存在於 entityEntries 中,此時 Hibernate 就認為 insertions  entityEntries 可能因為執行緒安全的問題產生了不同步(也不知道 Hibernate 的開發者是否考慮到例子中的處理方式,如果沒有的話,這也許算是一個 bug 吧),於是一個 net.sf.hibernate.AssertionFailure 就被丟擲,程式終止。 

         一般我們會錯誤的認為 s.save 會立即執行,而將物件過早的與 Session 拆離,造成了 Session  insertions  entityEntries 中內容的不同步。所以我們在做此類操作時一定要清楚 Hibernate 什麼時候會將資料 flush 入資料庫,在未 flush 之前不要將已進行操作的物件從 Session 上拆離。解決辦法是在 save 之後,新增 session.flush  

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述