1. 程式人生 > >Hibernate【緩存】知識要點

Hibernate【緩存】知識要點

常用 以及 provide 懶加載 更改 pub 語句 com 效率

對象狀態

Hibernate中對象的狀態:

  • 臨時/瞬時狀態
  • 持久化狀態
  • 遊離狀態

學習Hibernate的對象狀態是為了更清晰地知道Hibernate的設計思想,以及是一級緩存的基礎...當然啦,也就一點點知識

臨時/瞬時狀態

當我們直接new出來的對象就是臨時/瞬時狀態的..

  • 該對象還沒有被持久化【沒有保存在數據庫中】
  • 不受Session的管理

技術分享圖片

持久化狀態

當保存在數據庫中的對象就是持久化狀態了

  • 當調用session的save/saveOrUpdate/get/load/list等方法的時候,對象就是持久化狀態
  • 在數據庫有對應的數據
  • 受Session的管理
  • 當對對象屬性進行更改的時候,會反映到數據庫中!

技術分享圖片

我們來測試一下:當對對象屬性進行更改的時候,會反映到數據庫中!


        session.save(idCard);
        idCard.setIdCardName("我是測試持久化對象");

技術分享圖片

遊離狀態

當Session關閉了以後,持久化的對象就變成了遊離狀態了...

  • 不處於session的管理
  • 數據庫中有對應的記錄

技術分享圖片


一級緩存

Hibernate有一級緩存和二級緩存之分,這裏主要講解一級緩存

什麽是一級緩存?

Hibenate中一級緩存,也叫做session的緩存,它可以在session範圍內減少數據庫的訪問次數! 只在session範圍有效! Session關閉,一級緩存失效!

只要是持久化對象狀態的,都受Session管理,也就是說,都會在Session緩存中!

Session的緩存由hibernate維護,用戶不能操作緩存內容; 如果想操作緩存內容,必須通過hibernate提供的evit/clear方法操作

為什麽要是使用緩存?

減少對數據庫的訪問次數!從而提升hibernate的執行效率!

測試

我們來看一下Hibernate是怎麽減少對數據庫訪問的次數的。

現在我的User表有這麽一條記錄:

技術分享圖片

        //把數據放進cache
        User user = (User) session.get(User.class, 1);

        //發現要修改的字段和cache一樣,不執行
user.setUserName("你好2");

技術分享圖片

技術分享圖片

取數據也是一樣的


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

技術分享圖片


緩存相關的方法

和緩存有關常用的方法有三個:

  • session.flush(); 讓一級緩存與數據庫同步
  • session.evict(arg0); 清空一級緩存中指定的對象
  • session.clear(); 清空一級緩存中緩存的所有對象

  • clear


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

        //清除緩存,那麽下面獲取的時候,就不能從緩存裏面拿了
        session.clear();
        user = (User) session.get(User.class, 1);
  • flush

在有緩存的情況下,修改同一條記錄的數據,以最後的為準...因此只有一條update


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

        user.setUserName("我是第一");
        user = (User) session.get(User.class, 1);
        user.setUserName("我是第二");

技術分享圖片

我讓強制讓它和數據庫同步的話,就有兩條update了

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

        user.setUserName("我是第一");
        session.flush();
        user = (User) session.get(User.class, 1);
        user.setUserName("我是第二");

技術分享圖片

一般地,我們在批處理的時候會用,因為緩存也是有大小的,如果1000條數據插入進去都要緩存,那麽Hibernate可能就崩了...

  • 每隔一定記錄數,先與數據庫同步 flush()
  • 再清空緩存 clear()

值得註意的是:不同的Session是不會共享緩存的!

Iterator與list

我們使用HQL查詢全部數據的時候,可以使用list()得到所有的數據,也可以使用iterator()得到一個叠代器,再遍歷叠代器...那它們有什麽區別呢?

。。。。當時看視頻的時候說是下圖:

技術分享圖片

但是我在測試的時候:List也可以獲取緩存的數據

技術分享圖片

當然啦,Iterator也是可以獲取緩存的數據

技術分享圖片

因此,在獲取數據的時候還是使用list()方便!

懶加載

懶加載就是當使用數據的時候才去獲取數據、執行對應的SQL語句...當還沒用到數據的時候,就不加載對應的數據!

主要目的就是為了提高Hibernate的性能,提高執行效率

  • get: 及時加載,只要調用get方法立刻向數據庫查詢
  • load:默認使用懶加載,當用到數據的時候才向數據庫查詢。

懶加載再次體驗

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

        System.out.println("________");
        System.out.println(user);

技術分享圖片

我們可以在對應的配置文件用通常lazy屬性來設置

關閉懶加載:

    <class name="IdCard" table="IdCard" lazy="false">

技術分享圖片

lazy有三個屬性:

  • true 使用懶加載
  • false 關閉懶加載
  • extra (在集合數據懶加載時候提升效率)【只有在set、list等集合標簽中使用】
    • 在真正使用數據的時候才向數據庫發送查詢的sql;
    • 如果調用集合的size()/isEmpty()方法,只是統計,不真正查詢數據!

懶加載異常

當Session關閉後,就不能使用懶加載了,否則會報出異常


Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session

技術分享圖片

報出了這個異常,我們有4種方法解決:

  • 方式1: 先使用一下數據
    • dept.getDeptName();
  • 方式2:強迫代理對象初始化
    • Hibernate.initialize(dept);
  • 方式3:關閉懶加載
    • 設置lazy=false;
  • 方式4: 在使用數據之後,再關閉session!

Hibernate二級緩存

前面我們已經講解過了一級緩存,一級緩存也就是Session緩存,只在Session的範圍內有效...作用時間就在Session的作用域中,範圍比較小

Hibernate為我們提供了二級緩存功能:二級緩存是基於應用程序的緩存,所有的Session都可以使用

  • Hibernate提供的二級緩存有默認的實現,且是一種可插配的緩存框架!如果用戶想用二級緩存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影響代碼。
  • 如果用戶覺得hibernate提供的框架框架不好用,自己可以換其他的緩存框架或自己實現緩存框架都可以

技術分享圖片

Hibernate二級緩存:存儲的是常用的類


配置二級緩存

既然二級緩存是Hibernate自帶的,那麽我們可以在hibernate.properties文件中找到對應的信息..

技術分享圖片

  • hibernate.cache.use_second_level_cache false【二級緩存默認不開啟,需要手動開啟】
  • hibernate.cache.use_query_cache true 【開啟查詢緩存】
  • choose a cache implementation 【二級緩存框架的實現】
  • hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
  • hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
  • hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默認實現
  • hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
  • hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
  • hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider

通過配置文件我們可以發現,二級緩存默認是不開啟的,需要我們手動開啟,以下步驟:

  • 1)開啟二級緩存
  • 2)指定緩存框架
  • 3)指定哪些類加入二級緩存

開啟二級緩存

在hibernate.cfg.xml文件中開啟二級緩存


        <!-- a.  開啟二級緩存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>

指定緩存框架

指定Hibernate自帶的二級緩存框架就好了


        <!-- b. 指定使用哪一個緩存框架(默認提供的) -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>

指定哪些類加入二級緩存


        <!-- c. 指定哪一些類,需要加入二級緩存 -->
        <class-cache usage="read-write" class="zhongfucheng.aa.Monkey"/>
        <class-cache usage="read-only" class="zhongfucheng.aa.Cat"/>

測試:

我們知道一級緩存是Session的緩存,那麽我們在測試二級緩存的時候使用兩個Session來測試就好了。如果第二個Session拿到的是緩存數據,那麽就證明二級緩存是有用的。


package zhongfucheng.aa;

import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

public class App5 {
    public static void main(String[] args) {


        //獲取加載配置管理類
        Configuration configuration = new Configuration();
        //加載類對應的映射文件!
        configuration.configure().addClass(Animal.class);
        //創建Session工廠對象
        SessionFactory factory = configuration.buildSessionFactory();
        //得到Session對象
        Session session1 = factory.openSession();
        //使用Hibernate操作數據庫,都要開啟事務,得到事務對象
        Transaction transaction = session1.getTransaction();
        //開啟事務
        transaction.begin();
        Monkey monkey = (Monkey) session1.get(Monkey.class,"40283f815be67f42015be67f43240001" );
        System.out.println(monkey.getName());
        System.out.println("-----------------------");



        Session session2 = factory.openSession();
        Transaction transaction2 = session2.getTransaction();
        transaction2.begin();
        Monkey monkey2 = (Monkey) session1.get(Monkey.class, "40283f815be67f42015be67f43240001");
        System.out.println(monkey2.getName());


        //提交事務
        transaction.commit();
        transaction2.commit();

        //關閉Session
        session1.close();
        session2.close();


    }
}

得到的是緩存數據!

技術分享圖片


緩存策略

我們在把Animal類放進二級緩存的時候,用法為只讀

技術分享圖片

也就是說,只能讀取,不能寫入,我們來看看寫入會怎麽樣:


  monkey2.setName("小猴子");

拋出了異常....

技術分享圖片


usage的屬性有4種:

  • <class-cache usage="read-only"/> 放入二級緩存的對象,只讀;
  • <class-cache usage="nonstrict-read-write"/> 非嚴格的讀寫
  • <class-cache usage="read-write"/> 讀寫; 放入二級緩存的對象可以讀、寫;
  • <class-cache usage="transactional"/> (基於事務的策略)

集合緩存

如果我們在數據庫查詢的數據是集合...Hibernate默認是沒有為集合數據設置二級緩存的...因此還是需要去讀寫數據庫的信息

接下來,我們就看看把集合設置為二級緩存是什麽做的:

  • 在hibernate.cgf.xml中配置對象中的集合為二級緩存
        <!-- 集合緩存[集合緩存的元素對象,也加加入二級緩存] -->
        <collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>
  • 測試代碼:

    public void testCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // a. 查詢一次
        Dept dept = (Dept) session1.get(Dept.class, 10);
        dept.getEmps().size();// 集合
        session1.getTransaction().commit();
        session1.close();
        
        System.out.println("------");
        
        // 第二個session
        Session session2 = sf.openSession();
        session2.beginTransaction();
        // a. 查詢一次
        dept = (Dept) session2.get(Dept.class, 10);  // 二級緩存配置好; 這裏不查詢數據庫
        dept.getEmps().size();
        
        session2.getTransaction().commit();
        session2.close();
    }

查詢緩存

list()和iterator()會把數據放在一級緩存,但一級緩存只在Session的作用域中有效...如果想要跨Session來使用,就要設置查詢緩存

我們在配置文件中還看到了查詢緩存這麽一條配置..

    #hibernate.cache.use_query_cache true      【開啟查詢緩存】

也就是說,默認的查詢數據是不放在二級緩存中的,如果我們想要把查詢出來的數據放到二級緩存,就需要在配置文件中開啟

        <!-- 開啟查詢緩存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
  • 在使用程序查詢的時候,也要調用setCacheable()方法,設置為查詢緩存。

    @Test
    public void listCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // HQL查詢  【setCacheable  指定從二級緩存找,或者是放入二級緩存】
        Query q = session1.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());
        session1.getTransaction().commit();
        session1.close();
        
        
        Session session2 = sf.openSession();
        session2.beginTransaction();
        q = session2.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());  // 不查詢數據庫: 需要開啟查詢緩存
        session2.getTransaction().commit();
        session2.close();
    }

如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關註微信公眾號:Java3y

Hibernate【緩存】知識要點