1. 程式人生 > >hibernate物件狀態與一級快取二級快取

hibernate物件狀態與一級快取二級快取

1 hibernate 中 持久類物件狀態介紹

1.1hibernate 規定三種狀態:瞬時態、持久態、脫管態


    瞬時態:transient,     session沒有快取物件,資料庫也沒有對應記錄。
        OID特點:沒有值
    持久態:persistent,   session快取物件,資料庫最終會有記錄。(事務沒有提交)
        OID特點:有值
    脫管態:detached,    session沒有快取物件,資料庫有記錄。
        OID特點:有值

1.2 狀態之間的轉換

 

1.3  瞬時態/臨時態

  1. 獲得:一般都是直接建立(new
  2. 瞬時態---->持久態

       一般操作:save方法、saveOrUpdate

     3  瞬時態------>脫管態

       一般操作:通過setId方法設定資料

例如:

       User user = new User();        //瞬時態

       user.setUid(1);                       //脫管態

1.4 持久態

1.獲得:
               查詢操作:get、loat、createQuery、createCriteria 等 獲得都是持久態
                執行save之後持久態
               執行update之後持久態
2.   持久態------->瞬時態


                 官方規定執行delete()  --民間:刪除態
3. 持久態--------->脫管態
               session沒有記錄
               session.close () 關閉
                session.clear() 清除所有
                session.evict(obj) 清除指定的PO物件

1.5 脫管態/遊離態

1.獲得:
            建立、並設定OID的
             通過api獲得
2. 脫管態------->瞬時態
          手動去除OID,設定成預設值
3.脫管態-------->持久態


一般操作:update()、saveOrUpdate

1.6  測試

   @Test

   public void demo01(){

      User user = new User();  //瞬時態

      user.setUsername("jack");

      user.setPassword("1234"); //瞬時態(與oid沒有關係)

      Session session = factory.openSession();

      session.beginTransaction();

      session.save(user);   //持久態

//---- 持久態就應該有持久態的行為(特性)

//user.setUsername("rose");  //持久態物件 被修改後,hibernate將自動生成update語句

//session.flush();

      session.getTransaction().commit();

      session.close();

      System.out.println(user);  //脫管態

   }

2 一級快取

    一級快取:又稱為session級別的快取。當獲得一次會話(session),hibernate在session中建立多個集合(map),用於存放操作資料(PO物件),為程式優化服務,如果之後需要相應的資料,hibernate優先從session快取中獲取,如果有就使用;如果沒有再查詢資料庫。當session關閉時,一級快取銷燬。

2.1 證明一級快取

cfg.xml中配置資料庫語句顯示

測試一

@Test

   public void demo02(){

      //證明一級快取

      Session session = factory.openSession();

      session.beginTransaction();

      //1 查詢 id = 1

      User user = (User) session.get(User.class, 1);

      System.out.println(user);

      //2 再查詢 -- 不執行select語句,將從一級快取獲得

      User user2 = (User) session.get(User.class, 1);

      System.out.println(user2);

      session.getTransaction().commit();

      session.close();

   }

測試二

@Test

   public void demo03(){

      //清除快取

      Session session = factory.openSession();

      session.beginTransaction();

      User user = (User) session.get(User.class, 1);  //--select

      System.out.println(user);

      //清除

      //session.clear();

      session.evict(user);

      // 一級快取沒有快取物件,從資料庫直接查詢

      User user2 = (User) session.get(User.class, 1);  //--select

      System.out.println(user2);

      session.getTransaction().commit();

      session.close();

   }

2.2 一級快取快照

快照:

與一級快取一樣的存放位置,對一級快取資料備份。保證資料庫的資料與 一級快取的資料必須一致。

如果一級快取修改了,

執行commit提交時,將自動重新整理一級快取,

執行update語句,將一級快取的資料更新到資料庫

refresh 重新整理

refresh 保證 一級快取的資料 與 資料庫的資料 保持一致

將執行select語句查詢資料庫,將一級快取中的資料覆蓋掉只要執行refresh都將執行select語句。

@Test

   public void demo04(){

      //重新整理

      Session session = factory.openSession();

      session.beginTransaction();

      User user = (User) session.get(User.class, 1);  //--select

      System.out.println(user);

  session.refresh(user);

      session.getTransaction().commit();

      session.close();

   }

快照演示(一級快取重新整理)

@Test

   public void demo05(){

      //快照

      Session session = factory.openSession();

      session.beginTransaction();

      User user = (User) session.get(User.class, 1);  //--select

      System.out.println(user);

      //修改持久態物件內容(一級快取內容)--預設在commit時,將觸發update語句

      user.setUsername("rose2");

      session.getTransaction().commit();

      session.close();

   }

  1. 問題:一級快取什麼時候重新整理?(瞭解)

       預設情況提交(commit())重新整理。

@Test

   public void demo06(){

      //設定重新整理時機

      Session session = factory.openSession();

      session.beginTransaction();

      //1 設定

      session.setFlushMode(FlushMode.MANUAL);

      User user = (User) session.get(User.class, 1);

      user.setUsername("rose4");

      //1 查詢所有 -- AUTO , 查詢之前先更新,儲存一級快取和資料庫一樣的

      //List<User> allUser = session.createQuery("from User").list();

      //2手動重新整理 --MANUAL 將執行update,注意:一級快取必須修改後的

      session.flush();

      // 如果MANUAL 在執行commit 不進行update

      session.getTransaction().commit();

      session.close();

   }

三  二級快取

快取(Cache): 計算機領域非常通用的概念。它介於應用程式永久性資料儲存源(如硬碟上的檔案或者資料庫)之間,其作用是降低應用程式直接讀寫硬碟(永久性資料儲存源)的頻率,從而提高應用的執行效能。快取中的資料是資料儲存源中資料的拷貝。快取的物理介質通常是記憶體

       快取:程式<--(記憶體)-->硬碟

3.1 什麼是二級快取

  1. hibernate 提供快取機制:一級快取、二級快取

       一級快取:session級別快取,在一次請求中共享資料。

       二級快取:sessionFactory級別快取,整個應用程式共享一個會話工廠,共享一個二級快取。

  1. SessionFactory的快取兩部分:   內建快取:使用一個Map,用於存放配置資訊,預定義HQL語句等,提供給Hibernate框架自己使用,對外只讀的。不能操作。

     2  外接快取:使用另一個Map,用於存放使用者自定義資料。預設不開啟。外接快取hibernate只提供規範(介面),需要第三方實現類。外接快取有成為二級快取。

3.2 二級快取內部結構

二級就是由4部分構成
    類級別快取
    集合級別快取
    時間戳快取
    查詢快取(二級快取的第2大部分,三級快取)

3.3 併發訪問策略

  1. 訪問策略:讀寫型(read-write)、只讀型(read-only

3.4 應用場景

  1. 適合放入二級快取中的資料:

       很少被修改

       不是很重要的資料, 允許出現偶爾的併發問題

      2.不適合放入二級快取中的資料:

       經常被修改

       財務資料, 絕對不允許出現併發問題

       與其他應用資料共享的資料

3.5 二級快取提供商

  1. EHCache: 可作為程序(單機)範圍內的快取, 存放資料的物理介質可以是記憶體或硬碟, 對 Hibernate 的查詢快取提供了支援。--支援叢集。
  2. OpenSymphony `:可作為程序範圍內的快取, 存放資料的物理介質可以是記憶體或硬碟, 提供了豐富的快取資料過期策略, 對 Hibernate 的查詢快取提供了支援
  3. SwarmCache: 可作為叢集範圍內的快取, 但不支援 Hibernate 的查詢快取
  4. JBossCache:可作為叢集範圍內的快取, 支援 Hibernate 的查詢快取

 

X表示支援

3.6 ehcache配置二級快取

1.匯入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar

2.開啟二級快取(我要使用二級快取)

  1. 在hibernate.cfg.xml 配置二級快取

<!-- 9.1 開啟二級快取 -->

<property name="hibernate.cache.use_second_level_cache">true</property>

3.確定二級快取提供商(我要使用哪個二級快取)

  1. hibernate.cfg.xml 配置

<!-- 9.2 提供商 -->

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

4.確定需要快取內容

  在hibernate.cfg.xml 確定 類快取 和集合快取配置項

<!-- 9.3 確定快取內容 -->

      <!-- 類快取 -->

      <class-cache usage="read-write" class="com.test.Customer"/>

      <class-cache usage="read-write" class="com.test.Order"/>

      <!-- 集合快取 -->

   <collection-cacheusage="read-write"collection="com.itheima.a_init.Customer.orderSet"/>

       1>配置需要快取的類

       2>配置需要快取的集合

5.配置ehcache自定義配置檔案

步驟1:從jar包複製xml檔案

      

步驟2:將xml重新命名“ehcache.xml”

      

步驟3:將修改後的xml,拷貝到src下

      

6 測試

6.1 證明

@Test

   public void demo01(){

      //1 證明二級快取存在

      // * 修改toString()

      // * 如果二級快取開啟,查詢3 沒有select語句,表示從二級快取獲得的。

      // * 將二級快取關閉,查詢3將觸發select語句。

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 查詢id=1 -- 執行select (查詢後,將資料存放在一級快取,之後由一級快取同步到二級快取)

      Customer c1 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c1);

      //2 查詢id=1 --從一級快取獲取

      Customer c2 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c2);

      s1.getTransaction().commit();

      s1.close();

      System.out.println("----------");

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //3 查詢id=1 -- 從二級快取獲取

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

      s2.getTransaction().commit();

      s2.close();

   }

6.2 類快取

  1. 類快取:只存放資料
  2. 一級快取:存放物件本身

@Test

   public void demo02(){

      //2 類快取:只存放資料,散裝資料。

      // * 使用預設的toString();

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 查詢id=1 -- 執行select

      Customer c1 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c1);

      //2 查詢id=1 -- 從一級快取獲取,一級快取存放物件本身

      Customer c2 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c2);

      s1.getTransaction().commit();

      s1.close();

      System.out.println("----------");

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //3 查詢id=1 -- 物件不一樣,資料一樣

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

      s2.getTransaction().commit();

      s2.close();

   }

 6.3 集合快取

@Test

   public void demo03(){

       //3 集合快取:只存放關聯物件OID的值,如果需要資料,從類快取中獲取。

      // * 3.1 預設:第一條select 查詢客戶,第二天 select 查詢客戶所有訂單

      // * 3.2 操作:在hibernate.cfg.xml Order 類快取刪除

      // *** <!--  <class-cache usage="read-write" class="com.itheima.a_init.Order"/>-->

      // *** 多了10select,通過訂單的id查詢訂單

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 查詢id=1

      Customer c1 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c1);

      //2 獲得訂單

      for (Order o1 : c1.getOrderSet()) {

         System.out.println(o1);

      }

      s1.getTransaction().commit();

      s1.close();

      System.out.println("----------");

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //3 查詢id=1

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

      //4 獲得訂單

      for (Order o2 : c3.getOrderSet()) {

         System.out.println(o2);

      }

      s2.getTransaction().commit();

      s2.close();

   }

7 時間戳

  1. 時間戳:任何操作都在時間戳中記錄操作時間。

 

@Test

   public void demo04(){

      //4 時間戳: 所有的操作都會在時間戳中進行記錄,如果資料不一致,將觸發select語句進行查詢

      // * 修改toString()

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 查詢id=1

      Integer cid = 1;

      Customer c1 = (Customer) s1.get(Customer.class, cid);

      System.out.println(c1);

      //2 繞過一級和二級快取,修改資料庫,修改客戶cname=大東哥

      s1.createQuery("update Customer set cname = ? where cid = ?")

         .setString(0, "大東哥")

         .setInteger(1, cid)

         .executeUpdate();

      //3列印

      System.out.println(c1);

      s1.getTransaction().commit();

      s1.close();

      System.out.println("----------");

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //4 查詢id=1  -- ?

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

      s2.getTransaction().commit();

      s2.close();

   }

 8 查詢快取

  1. 查詢快取又稱為三級快取(民間)
  2. 查詢快取預設不使用。需要手動開啟
  3. 查詢快取:將HQL語句與 查詢結果進行繫結。通過HQL相同語句可以快取內容。

       預設情況Query物件只將查詢結果存放在一級和二級快取,不從一級或二級快取獲取。

       查詢快取就是讓Query可以從二級快取獲得內容。

步驟一:開啟查詢快取

      

   <!-- 9.4 開啟查詢快取 -->

      <property name="hibernate.cache.use_query_cache">true</property>

步驟二:在查詢query物件,設定快取內容(注意:存放和查詢 都需要設定)

      

             

@Test

   public void demo05(){

      //5 查詢快取

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 query查詢

      Query q1 = s1.createQuery("from Customer");

      q1.setCacheable(true);

      List<Customer> a1 = q1.list();

      for (Customer c1 : a1) {

         System.out.println(c1);

      }

      //2 cid =1  -- 一級快取獲得

      Customer customer = (Customer) s1.get(Customer.class, 1);

      System.out.println(customer);

      s1.getTransaction().commit();

      s1.close();

      System.out.println("----------");

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //2 cid =1  -- 二級快取獲得

      Customer customer2 = (Customer) s2.get(Customer.class, 1);

      System.out.println(customer2);

      //3 query查詢

      Query q2 = s2.createQuery("from Customer");

      q2.setCacheable(true);

      List<Customer> a2 = q2.list();

      for (Customer c2 : a2) {

         System.out.println(c2);

      }

      s2.getTransaction().commit();

      s2.close();

   }

9 ehcache配置檔案

  1. <diskStore path="java.io.tmpdir"/>  設定臨時檔案存放位置。(快取一般記憶體,一定程度時,寫入硬碟。)

      

  1. 快取詳細設定

       <defaultCache> 所有的快取物件預設的配置

       <cache name="類"> 指定物件單獨配置

  1. 引數設定

           maxElementsInMemory="10000"  記憶體最大數

           eternal="false"  是否永久(記憶體常駐留)

           timeToIdleSeconds="120"

          timeToLiveSeconds="120"

          overflowToDisk="true"  記憶體滿了,是否寫入到硬碟

            maxElementsOnDisk="10000000"  硬碟最大數

         diskPersistent="false"  關閉JVM,是否將記憶體儲存硬碟中

         diskExpiryThreadIntervalSeconds="120"  輪詢

           memoryStoreEvictionPolicy="LRU"

       Least Recently Used (specified as LRU).

       First In First Out (specified as FIFO)

       Less Frequently Used (specified as LFU)

    • maxElementsInMemory :設定基於記憶體的快取中可存放的物件最大數目
  • eternal:設定物件是否為永久的,true表示永不過期,此時將忽略timeToIdleSeconds timeToLiveSeconds屬性; 預設值是false
  • timeToIdleSeconds:設定物件空閒最長時間,以秒為單位, 超過這個時間,物件過期。當物件過期時,EHCache會把它從快取中清除。如果此值為0,表示物件可以無限期地處於空閒狀態。
  • timeToLiveSeconds:設定物件生存最長時間,超過這個時間,物件過期。
    如果此值為0,表示物件可以無限期地存在於快取中. 該屬性值必須大於或等於 timeToIdleSeconds 屬性值
  • overflowToDisk:設定基於內在的快取中的物件數目達到上限後,是否把溢位的物件寫到基於硬碟的快取中
  • diskPersistent 當jvm結束時是否持久化物件 true false 預設是false
  • diskExpiryThreadIntervalSeconds 指定專門用於清除過期物件的監聽執行緒的輪詢時間
  •  memoryStoreEvictionPolicy - 當記憶體快取達到最大,有新的element加入的時候,移除快取中element的策略。預設是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)