第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Hibernate 性能之緩存

1. 前言

本節(jié)課程和大家一起聊聊性能優(yōu)化方案之:緩存。通過本節(jié)課程學(xué)習(xí),你將了解到:

  • 什么是緩存,緩存的作用;
  • HIbernate 中的緩存級(jí)別;
  • 如何使用緩存。

2. 緩存

2.1 緩存是什么

現(xiàn)實(shí)世界里,緩存是一個(gè)無處不在的概念。

家里的米桶中都會(huì)儲(chǔ)存大米,需要下鍋時(shí),直接從米桶里拿出來,而不是等米下鍋時(shí)去商店采購。只有等到米桶中沒有米時(shí),才會(huì)去商店。

米桶就是一個(gè)類似于緩存的存儲(chǔ)體,它的作用是用來緩存大米。

程序中,通俗講,緩存就是一個(gè)用來臨時(shí)存儲(chǔ)數(shù)據(jù)的地方,便于需要時(shí)伸手便可拿到。

更專業(yè)上講,緩存可以在兩個(gè)速度不匹配的設(shè)備之間建立一個(gè)緩沖帶,適配兩者速度。

2.2 Hibernate 中的為什么需要緩存

要搞清楚 Hibernate 為什么需要緩存,那就要了解 Hibernate 使用緩存做什么?

Hibernate 的任務(wù)是幫助開發(fā)者發(fā)送 SQL 語句,從數(shù)據(jù)庫中獲取數(shù)據(jù)。

這個(gè)過程并不輕松。從微觀角度上講,Hibernate 要背上行李,通過縱橫交織的網(wǎng)絡(luò)交通,到達(dá)數(shù)據(jù)庫服務(wù)器,獲取數(shù)據(jù)。然后背起數(shù)據(jù),繼續(xù)行走在四通八達(dá)的網(wǎng)絡(luò)交通,回到程序中。

運(yùn)氣不好時(shí),碰到網(wǎng)絡(luò)擁堵,就會(huì)產(chǎn)生延遲,遇到網(wǎng)絡(luò)斷線,則會(huì)丟失數(shù)據(jù)。

理論上講,對(duì)于每次的數(shù)據(jù)請(qǐng)求,這個(gè)過程都是必須的。

但是,如果多次的請(qǐng)求是同樣數(shù)據(jù)的時(shí)候,也就是用戶的請(qǐng)求 SQL 是一樣的時(shí)候,有必要這么不停地來往于數(shù)據(jù)庫服務(wù)器嗎?

面對(duì)這種情況,Hibernate 提供的緩存就起作用了,可以緩存曾經(jīng)從數(shù)據(jù)庫中獲取過的數(shù)據(jù)。如果下次再需要時(shí),只需要從緩存中獲取,而無需翻山涉水,通過網(wǎng)絡(luò)獲取。

圖片描述

Hibernate 的緩存主要是存儲(chǔ)曾經(jīng)操作過的數(shù)據(jù),程序邏輯向 Hibernate 發(fā)送數(shù)據(jù)請(qǐng)求操作時(shí),Hibernate 會(huì)先查詢緩存中有沒有,如果存在,則直接從緩存中獲取,沒有時(shí),才會(huì)行走于網(wǎng)絡(luò)通道,從數(shù)據(jù)庫中獲取。

3. Session 緩存

Hibernate 提供有一級(jí)和二級(jí)緩存,一級(jí)緩存也叫 Session 緩存,二級(jí)緩存也叫 SessionFactory 緩存。

前面課程中和大家聊過,Session 的使用原則是,需要時(shí)創(chuàng)建,用完后關(guān)閉,其作用域一般為方法級(jí)別。

一級(jí)緩存的生命周期和 Session 是一致的,所以,一級(jí)緩存中所存儲(chǔ)的數(shù)據(jù)其生命周期也不長,其實(shí)際意義就論情況來看了。

SessionFactory 在前面也討論過,SessionFactory 是應(yīng)用程序級(jí)別的生命周期,所以與其關(guān)聯(lián)的緩存中所保存的數(shù)據(jù)也可以長時(shí)間存在。

默認(rèn)情況下,Hibernate 的一級(jí)緩存是可以直接使用的,二級(jí)緩存是沒有打開的。需要根據(jù)實(shí)際情況進(jìn)行選擇。

驗(yàn)證一級(jí)緩存

需求:在 Session 關(guān)閉之前,連續(xù)查詢相同的學(xué)生兩次。

Session session = sessionFactory.openSession();
Transaction transaction = null;
Student stu = null;
try {
	transaction = session.beginTransaction();
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
	// 查詢前面查詢過的學(xué)生
	System.out.println("--------------第二次查詢------------------");
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
	transaction.commit();
} catch (Exception e) {
	transaction.rollback();
} finally {
	session.close();
}

運(yùn)行結(jié)果如下:

Hibernate: 
    select
        student0_.stuId as stuId1_3_0_,
        student0_.classRoomId as classRoo6_3_0_,
        student0_.stuName as stuName2_3_0_,
        student0_.stuPassword as stuPassw3_3_0_,
        student0_.stuPic as stuPic4_3_0_,
        student0_.stuSex as stuSex5_3_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate
--------------第二次查詢------------------
Hibernate

從輸出結(jié)果中能得到什么結(jié)論?

只有在第一次查詢的時(shí)候,Hibernate 才會(huì)向數(shù)據(jù)庫發(fā)送 SQL 語句請(qǐng)求,第二查詢時(shí),不需要再發(fā)送 SQL 請(qǐng)求,因?yàn)榫彺嬷幸呀?jīng)存在。

稍微改動(dòng)一下上述實(shí)例,創(chuàng)建兩個(gè) Session 對(duì)象,用來查詢同一個(gè)學(xué)生:

Session session = sessionFactory.openSession();
Transaction transaction = null;
Student stu = null;
try {
	transaction = session.beginTransaction();
	System.out.println("--------------第一次查詢------------------");
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
} catch (Exception e) {
	transaction.rollback();
} finally {
	session.close();
}

session = sessionFactory.openSession();
try {
	transaction = session.beginTransaction();
	// 查詢前面查詢過的學(xué)生
	System.out.println("--------------第二次查詢------------------");
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
	transaction.commit();
} catch (Exception e) {
	transaction.rollback();
} finally {
	session.close();
}

查看控制臺(tái)上的輸出結(jié)果:

Hibernate: 
    select
        student0_.stuId as stuId1_3_0_,
        student0_.classRoomId as classRoo6_3_0_,
        student0_.stuName as stuName2_3_0_,
        student0_.stuPassword as stuPassw3_3_0_,
        student0_.stuPic as stuPic4_3_0_,
        student0_.stuSex as stuSex5_3_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate
--------------第二次查詢------------------
Hibernate: 
    select
        student0_.stuId as stuId1_3_0_,
        student0_.classRoomId as classRoo6_3_0_,
        student0_.stuName as stuName2_3_0_,
        student0_.stuPassword as stuPassw3_3_0_,
        student0_.stuPic as stuPic4_3_0_,
        student0_.stuSex as stuSex5_3_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate

每次查詢都會(huì)發(fā)送 SQL 請(qǐng)求,這是因?yàn)?Session 緩存中的數(shù)據(jù)只能提供給本 Session 對(duì)象使用。不能跨 Session 使用。

  • 當(dāng)調(diào)用 save ()、update () 或 saveOrUpdate () 方法傳遞一個(gè)對(duì)象時(shí),或使用 load ()、 get ()、list ()、iterate () 方法獲得一個(gè)對(duì)象時(shí),該對(duì)象都將被加入到 Session 的內(nèi)部緩存中;
  • 可以通過調(diào)用 close()、clear()、evict() 方法手工清空緩存中的數(shù)據(jù)。

前面說過的,Session 生命周期很短,與 Session 關(guān)聯(lián)的一級(jí)緩存的生命周期也很短,所以緩存的命中率是很低的。其對(duì)系統(tǒng)性能的改善也有限得很。Session 內(nèi)部緩存的主要作用是保持 Session 內(nèi)部數(shù)據(jù)狀態(tài)同步。

4. SessionFactory 緩存

SessionFactory 緩存也稱其為二級(jí)緩存,是應(yīng)用程序級(jí)別的緩存。二級(jí)緩存在默認(rèn)情況下是沒有啟動(dòng)的,如果開發(fā)者想使用二級(jí)緩存所提供的功能,則需要通過一系列的操作流程方能讓其現(xiàn)身。

Hibernate 本身也提供有二級(jí)緩存的功能模塊,但只建議用于測試或?qū)W習(xí)過程。對(duì)于生產(chǎn)環(huán)境,Hibernae 建議使用專業(yè)的第三方緩存框架,如 EhCache 緩存框架。

常用緩存框架:

  • EhCache;
  • OSCache;
  • SwarmCache;
  • JBossCache。

啟動(dòng)二級(jí)緩存

  1. Hibernate 的主配置文件中啟動(dòng)并指定二級(jí)緩存的實(shí)現(xiàn)者;
<property name="cache.use_structured_entries">true</property>
<property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  1. 添加 EhCache 相關(guān)的 JAR 包。這些 JAR 包都可以在下載的 Hibernate 框架包的 lib 文件夾中找到;
  • ehcache-core-2.4.3.jar;
  • hibernate-ehcache-4.2.0.Final.jar。
  1. 在項(xiàng)目的 src 中添加 EhCache 緩存框架的配置文件 ehcache.xml

這個(gè)配置文件可以在下載的 Hibernate 框架包中的 project 目錄下的 etc 中找到。此配置文件中的內(nèi)容用來配置緩存管理相關(guān)信息。

<ehcache>  
<diskStore path="java.io.tmpdir"/>  
<defaultCache  
        maxElementsInMemory="10000"  
        eternal="false"  
        timeToIdleSeconds="120"  
        timeToLiveSeconds="120"  
        overflowToDisk="true"  
        />  
</ehcache>  

配置說明:

  • maxElementsInMemory: 緩存最大數(shù)目;
  • eternal : 緩存是否持久;
  • overflowToDisk : 是否保存到磁盤,當(dāng)系統(tǒng)當(dāng)機(jī)時(shí);
  • timeToIdleSeconds : 當(dāng)緩存閑置 n 秒后銷毀;
  • timeToLiveSeconds : 當(dāng)緩存存活 n 秒后銷毀。
  1. 在需要緩存的實(shí)體類上添加 @cache 注解
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL,include="all",region="student")  

只有被 @Cache 注解的實(shí)體才會(huì)被存儲(chǔ)進(jìn)二級(jí)緩存中,此注解有一個(gè) usage 屬性,用來配置緩存的策略,是一個(gè)枚舉類型,有如下幾種選擇:

  • CacheConcurrencyStrategy.NONE;
  • CacheConcurrencyStrategy.NONSTRICT_READ_WRITE: 非嚴(yán)格讀寫緩存;
  • CacheConcurrencyStrategy.READ_ONLY: 只讀緩存;
  • CacheConcurrencyStrategy.READ_WRITE: 讀寫緩存;
  • CacheConcurrencyStrategy.TRANSACTIONAL: 事務(wù)緩存。

Region 指定二級(jí)緩存中的區(qū)域名,默認(rèn)為類或者集合的名字。
include 有幾個(gè)選項(xiàng),non-lazy 當(dāng)屬性延遲抓取打開時(shí),標(biāo)記為 lazy=“true” 的實(shí)體的屬性可能無法被緩存。

做完上面的事情后,再執(zhí)行前面的兩個(gè) Session 對(duì)象查詢同一個(gè)學(xué)生的代碼,再查看控制臺(tái)上的信息:

Hibernate: 
    select
        student0_.stuId as stuId1_1_0_,
        student0_.classRoomId as classRoo5_1_0_,
        student0_.stuName as stuName2_1_0_,
        student0_.stuPassword as stuPassw3_1_0_,
        student0_.stuSex as stuSex4_1_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
學(xué)生姓名:Hibernate
--------------第二次查詢------------------
學(xué)生姓名:Hibernate

第一次查詢時(shí),需要發(fā)送 SQL 請(qǐng)求,第二查詢時(shí),不再發(fā)送 SQL 請(qǐng)求,因?yàn)椴樵冞^的信息已經(jīng)被存儲(chǔ)在了二級(jí)緩存中,Hibernate 會(huì)直接從緩存查詢。

二級(jí)緩存并不支持緩存 Blob 類型的數(shù)據(jù)。

5. 小結(jié)

本節(jié)課和大家一起了解了 Hibernate 提供的緩存機(jī)制,Hibernate 提供了一級(jí)緩存和二級(jí)緩存。一級(jí)緩存因生命周期較短,主要用于內(nèi)部服務(wù)。二級(jí)緩存因生命周期較長,命中率會(huì)較高,緩存中一般存放經(jīng)常被訪問、改動(dòng)不頻繁、數(shù)量有限的數(shù)據(jù)。

有了緩存機(jī)制的加持,HIbernate 在響應(yīng)開發(fā)者的請(qǐng)求時(shí),又會(huì)少了許多延遲。速度對(duì)于程序來講,是一個(gè)重要的性能指標(biāo)。