1. 程式人生 > >Hibernate旅程(七)Hibernate快取機制--一級快取

Hibernate旅程(七)Hibernate快取機制--一級快取

Hibernate一級快取

快取就是你去小賣鋪買東西,不用再去生產車間裡買東西,當小賣鋪倒閉了,也就是session快取生命週期結束。hibernate一級快取的宣告週期很短,和session的生命週期一致,hibernate的一級快取也叫做session級快取,或叫事務級快取。下面來看session控制的一級快取。

同一session中使用兩次load()進行查詢。

程式碼入下所示,我們在同一個session中兩次呼叫load()。

/**
    * 在同一個session中發出兩次load查詢
    */
   public voidtestCache1() {
      Sessionsession = null;
      try {
         //使用load查詢兩遍.
         session= HibernateUtils.getSession();
         session.beginTransaction();
         Studentstudent =(Student)session.load(Student.class, 1);
         System.out.println("student.name=" + student.getName());
        
         //不會發出查詢語句,load使用快取,在同一個session中.
         student =(Student)session.load(Student.class, 1);
         System.out.println("student.name=" + student.getName());
        
         session.getTransaction().commit();
      }catch(Exceptione) {
         e.printStackTrace();
         session.getTransaction().rollback();
      }finally {
         HibernateUtils.closeSession(session);
      }
   }    

兩次都採用load()進行載入並打印出學生的名字。發出的sql語句如下所示。對於loadlazy載入,只有在使用load載入上來的類,才真正的去資料庫查詢。控制檯列印的sql程式碼如下所示。


從顯示結果中我們可以看出,在第一次真正使用load的時候,發出sql語句。Hibernate會把查詢上來真實的物件放到sessionmap中。當第二次load再次使用的時候,不會再發送sql語句,而是直接從session的快取中取出。

同一session中使用兩次get()進行查詢。

原始碼也就是把上述load換成get

Getload的區別,get不支援延遲載入而load支援延遲載入,但當我們在同一次訪問中訪問兩次

get方法時,可以看到當載入get方法的時候控制檯立刻列印sql,同時放到了快取中。當我們第一次呼叫get方法的時候,同樣和load方法一樣,沒有再次去資料庫中查詢,而是直接從快取中取出來顯示。列印結果如下圖所示。



在同一session中發出兩次iterate查詢,查詢實體物件。

程式碼如下所示。

/**
    * 在同一個session中發出兩次iterate查詢,查詢實體物件
    * 發出兩次迭代查詢.查詢實體物件.
    */
   public void testCache3() {
      Sessionsession = null;
      try {
         session= HibernateUtils.getSession();
         session.beginTransaction();
         Iteratoriter = session.createQuery("from Student s where s.id<5").iterate();
         while(iter.hasNext()) {
            Studentstudent = (Student)iter.next();
            System.out.println(student.getName());
         }
         System.out.println("--------------------------------------");
         //它會發出查詢id的語句,但不會發出根據id查詢學生的語句,因為iterate使用快取
         iter= session.createQuery("from Student s where s.id<5").iterate();
         while(iter.hasNext()) {
            Studentstudent = (Student)iter.next();
            System.out.println(student.getName());
         }
         session.getTransaction().commit();
      }catch(Exceptione) {
         e.printStackTrace();
         session.getTransaction().rollback();
      }finally {
         HibernateUtils.closeSession(session);
      }
   } 


執行結果如下所示。



第一次呼叫迭代器的查詢時,首先發出查詢id的語句,並根據id查詢學生。當第二次呼叫時,只會發出查詢id的語句,不會再根據id來查詢對應的學生物件。這說明iterate(迭代器)是支援快取的。

在同一session中發出兩次iterate查詢,查詢實體物件。

程式碼如下所示。

Session session = null;
      try {
         session= HibernateUtils.getSession();
         session.beginTransaction();
         Iteratoriter = session.createQuery("select s.name from Student s wheres.id<5").iterate();
         while(iter.hasNext()) {
            Stringname = (String)iter.next();
            System.out.println(name);
         }
         System.out.println("--------------------------------------");
        
         //iterate查詢普通屬性,一級快取不會快取,所以發出查詢語句
         //一級快取是快取實體物件的
         iter= session.createQuery("select s.name from Student s wheres.id<5").iterate();
         while(iter.hasNext()) {
            Stringname = (String)iter.next();
            System.out.println(name);
         }
         session.getTransaction().commit();


顯示結果如下所示。



根據顯示結果可知,迭代器查詢普通屬性,一級快取不會儲存,所以當第二次查詢的時候仍然發出查詢語句。這說明iterate一級快取快取的是實體物件,對於普通屬性不會快取

在兩個session中發出load查詢。

程式碼如下所示。

Session session = null;
      try {
         session = HibernateUtils.getSession();
         session.beginTransaction();
         Studentstudent = (Student)session.load(Student.class, 1);
         System.out.println("student.name=" +student.getName());
         session.getTransaction().commit();
      }catch(Exceptione) {
         e.printStackTrace();
         session.getTransaction().rollback();
      }finally {
         HibernateUtils.closeSession(session);
      }
     
      try {
         session = HibernateUtils.getSession();
         session.beginTransaction();
         Studentstudent = (Student)session.load(Student.class, 1);
         //會發出查詢語句,session間不能共享一級快取資料
         //因為他會伴隨著session的消亡而消亡
         System.out.println("student.name=" +student.getName());
         session.getTransaction().commit();
 


顯示結果如下所示。



從上圖可以看出,會發出兩次sql,這也說明了session之間不能共享一級快取資料,因為快取會本隨著自己的那個session的消亡而消亡

在一個session中先呼叫save(),再呼叫getload查詢剛剛save的資料。

程式碼如下所示。

/**
    * 在同一個session中先呼叫save,再呼叫load查詢剛剛save的資料
    */
   public voidtestCache6() {
      Sessionsession = null;
      try {
         session= HibernateUtils.getSession();
         session.beginTransaction();
         Studentstudent = new Student();
         student.setName("張三");
         Serializableid = session.save(student);
         student= (Student)session.load(Student.class,id);
         //不會發出查詢語句,因為save支援快取
         System.out.println("student.name=" +student.getName());
         session.getTransaction().commit();
      }catch(Exceptione) {
         e.printStackTrace();
         session.getTransaction().rollback();
      }finally {
         HibernateUtils.closeSession(session);
      }
   } 
 


顯示結果如下所示。



從圖中可以看出,只發送一次插入語句,當我們再次查詢的時候,沒有去資料庫進行查詢,這說明當使用session.save()時,已經放入快取中。再進行查詢時會從快取中取出。

大批量資料的新增。

程式碼如下所示。

public voidtestCache7() {
      Sessionsession = null;
      try {
         session= HibernateUtils.getSession();
         session.beginTransaction();
         for (int i=0;i<100; i++) {
            Studentstudent = newStudent();
            student.setName("張三" +i);
            session.save(student);
            //每20條更新一次
            if (i %20 == 0) {
                session.flush();
                //清除快取的內容
                session.clear();
            }
         }
         session.getTransaction().commit();
      }catch(Exceptione) {
         e.printStackTrace();
         session.getTransaction().rollback();
      }finally {
         HibernateUtils.closeSession(session);
      }
   }       


列印sql如下圖所示。



從圖中可知,列印了100inset語句,在一個session中快取100條資料很大,我們可以設定每20條清空快取。

Hibernate一級快取總結

從上可知,loadgetiterate查詢實體物件時,支援一級快取,但查詢普通屬性時不支援一級快取,當我們大批量資料插入或更新時,由於快取中資料量太大,我們可以設定快取中的條數,使用session.clear()來清除快取。