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
- public void testClear(){
- Session session = HibernateUitl.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Teacher t = (Teacher) session.get(Teacher.class, 3);
- System.out.println(t.getName());
- Teacher t2 = (Teacher) session.get(Teacher.class, 3);
- System.out.println(t2.getName());
- session.getTransaction().commit();
- }
這裡雖然用了2 個 get 方法( get 方法會立即執行 sql 語句),但因為第一次執行了會快取一個 ID 為 3 的實體,所以雖然有 2 個 get 方法只執行一次 SQL 語句。
[c-sharp] view plain copy print ?
- public void testClear(){
- Session session = HibernateUitl.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Teacher t = (Teacher) session.get(Teacher.class, 3);
- System.out.println(t.getName());
- session.clear();//這裡不clear只會執行一次sql語句,有clear會執行2次
- Teacher t2 = (Teacher) session.get(Teacher.class, 3);
- System.out.println(t2.getName());
- session.getTransaction().commit();
- }
這裡在第2 次 get 前執行 session.clear(), 我們把 hibernate show_sql 出來,它就會執行 2 次 sql 語句了。 所以session.clear() 會清除快取。
2.Flush方法
可以強制進行從記憶體到資料庫的同步。
例:
[c-sharp] view plain copy print ?
- @Test
- /**
- * flush 強制與資料庫同步
- */
- public void testFlush(){
- Session session = HibernateUitl.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Teacher t = (Teacher) session.get(Teacher.class, 3);
- t.setName("yyy");
- t.setName("yyyyy");
- session.getTransaction().commit();
- }
看這段程式碼,我們setName() 2 次, 但程式只會更改資料庫一次,在 commit 時。
[c-sharp] view plain copy print ?
- @Test
- /**
- * flush 強制與資料庫同步
- */
- public void testFlush(){
- Session session = HibernateUitl.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Teacher t = (Teacher) session.get(Teacher.class, 3);
- t.setName("yyy");
- session.flush();//有flush會執行2次UPDAE,沒有會只執行一次
- t.setName("yyyyy");
- session.getTransaction().commit();
- }
我們在第2 次 setName ()時 執行 session.flush().
再看hibernate 執行的 sql 語句
[c-sharp] view plain copy print ?
- Hibernate:
- update
- Teacher
- set
- birthday=?,
- name=?,
- title=?
- where
- id=?
- Hibernate:
- update
- Teacher
- set
- birthday=?,
- name=?,
- title=?
- where
- 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 。