2015年10月30日 星期五

一級快取與Session.flush() (Hibernate3.x 八)

一級快取,又名Session級快取,因為生命週期和Session一樣,Session消毀,它也同時消毀;可用 clear 和 evict 管理

※一級快取

Session s1 = HibernateUtil2.getSession();
Session s2 = HibernateUtil2.getSession();
try {
    Emp e1 = (Emp) s1.get(Emp.class, 7369);
    System.out.println("first:" + e1.getEname());
    Emp e2 = (Emp) s2.get(Emp.class, 7369);
    System.out.println("second:" + e2.getEname());
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    if(s1.isOpen()){
        s1.close();
    }
    if(s2.isOpen()){
        s2.close();
    }
}

※以控制台出現訊息為標準,預設就有一級快取,因為只有Select一次


SessionFactory sf = HibernateUtil2.getSessionFactory();
Session s1 = sf.openSession();
Session s2 = sf.openSession();
    
Emp e1 = (Emp) s1.get(Emp.class, 7369);
System.out.println("first:" + e1.getEname());
Emp e2 = (Emp) s2.get(Emp.class, 7369);
System.out.println("second:" + e2.getEname());

※有快取是因為基於同一個Session,像上面的程式碼,有兩個Session,就沒有快取了,會輸出兩次Select


Session s = HibernateUtil2.getSession();
    
Emp emp = new Emp();
emp.setEmpno(7369);
emp.setEname("XXX");
s.update(emp);
s.beginTransaction().commit();
    
Emp e = (Emp) s.get(Emp.class, 7369);
System.out.println("xxx:" + e.getEname());

※update時也一樣有快取,此時控制台輸出update語句,但沒有Select,照樣查詢的到
※save和delete我試的結果,在控制台除了insert和delete語句外,還是有Select語句

※管理快取

尤於上面的程式碼會快取,但如果有100萬條或更多的記錄也把它快取下來,這樣子極有可能造成效能變低和OutOfMemoryException,記憶體爆了,所以有以下兩種方法來管理快取


Session s = HibernateUtil2.getSession();
    
Emp emp = new Emp();
emp.setEmpno(7369);
emp.setEname("SMITH");
s.update(emp);
s.beginTransaction().commit();
    
s.clear();
    
Emp e = (Emp) s.get(Emp.class, 7369);
System.out.println("second:" + e.getEname());


※可以用clear清除全部快取,所以查詢有語句


Session s = HibernateUtil2.getSession();
    
//    Emp e1 = (Emp) s.get(Emp.class, 7369);
//    System.out.println("e1:" + e1.getEname());
        
Emp e1 = new Emp();
e1.setEmpno(7369);
e1.setEname("SMITH");
s.update(e1);
s.beginTransaction().commit();
    
Dept d1 = (Dept) s.get(Dept.class, 20);
System.out.println("d1:" + d1.getDname());
    
s.evict(e1);
    
Emp e2 = (Emp) s.get(Emp.class, 7369);
System.out.println("e2:" + e2.getEname());
    
Dept d2 = (Dept) s.get(Dept.class, 20);
System.out.println("d2:" + d2.getDname());

※如果只想清除某個物件的快取,就要用evict,像我是將e1的快取清除,所以控制台輸出update語句,然後輸出Select Dept的語句,但因為有evict(e1),所以Select Emp也輸出了

※如果有二級快取,就沒有效果了


※Session.flush()

Session s = HibernateUtil2.getSession();
    
Emp emp = new Emp();
emp.setEmpno(7369);
emp.setEname("SMITH");
s.update(emp);
s.flush();
// s.beginTransaction().commit();

※像上面的程式碼,因為commit註解掉,控制台理論上是空白的,但因為有flush(),所以會輸出語句,但因為沒commit(),所以資料庫沒作用

※用commit(),其實也會flush後再提交

※flush總共有五種模式,先看原始碼的註解怎麼說
/**
 * The {@link Session} is never flushed unless {@link Session#flush}
 * is explicitly called by the application. This mode is very
 * efficient for read only transactions.
 *
 * @deprecated use {@link #MANUAL} instead.
 */
public static final FlushMode NEVER = new FlushMode( 0, "NEVER" );
    
/**
 * The {@link Session} is only ever flushed when {@link Session#flush}
 * is explicitly called by the application. This mode is very
 * efficient for read only transactions.
 */
public static final FlushMode MANUAL = new FlushMode( 0, "MANUAL" );
    
/**
 * The {@link Session} is flushed when {@link Transaction#commit}
 * is called.
 */
public static final FlushMode COMMIT = new FlushMode(5, "COMMIT");
    
/**
 * The {@link Session} is sometimes flushed before query execution
 * in order to ensure that queries never return stale state. This
 * is the default flush mode.
 */
public static final FlushMode AUTO = new FlushMode(10, "AUTO");
    
/**
 * The {@link Session} is flushed before every query. This is
 * almost always unnecessary and inefficient.
 */
public static final FlushMode ALWAYS = new FlushMode(20, "ALWAYS");

※NEVER:此模式不能flush,除非明確呼叫應用程式,在只有唯讀時,效能很高,此模式已廢棄了,要使用MANUAL代替

※MANUAL:當明確呼叫應用程式,此模式永遠flush,在唯讀時,效能很高

※COMMIT:當呼叫commit時,清除快取

※AUTO:此Session查詢執行之前有時會flush,以確保查詢不會返回已經失效的狀態,這是預設的選項

※ALWAYS:在所有查詢之前都會清除快取,這幾乎是不必要且效率低的

Session s = HibernateUtil2.getSession();
// 五種模式
s.setFlushMode(FlushMode.AUTO);
s.setFlushMode(FlushMode.ALWAYS);
s.setFlushMode(FlushMode.COMMIT);
s.setFlushMode(FlushMode.MANUAL);
s.setFlushMode(FlushMode.NEVER);
    
System.out.println("first");
Emp e1 = (Emp) s.load(Emp.class, 7369);
s.beginTransaction().commit();
    
System.out.println("second");
Emp e2 = (Emp) s.get(Emp.class, 7369);
s.beginTransaction().commit();


※整理如下:
.NEVER:等同MANUAL,因以廢棄,註解也要我們使用MANUAL

.MANUAL:不使用flush

.COMMIT:commit前flush

.AUTO:這個我不知如何試,註解也是說有時會,什麼是「有時會」並沒有說清楚

.ALWAYS:這個我試不出來,以上面的程式碼來說,我用load,畫面還是一片空白,不過反正註解都說是效率低的,就不要用就好了

找到這篇文章,已後有時間再測

沒有留言:

張貼留言