Hibernate——緩存

一、對象狀態

參考:https://mp.weixin.qq.com/s/VFnny3ou48w9bIgTT0xVow

1.1 臨時/瞬時狀態

含義:直接new出來的對象就是臨時/瞬時狀態的。
特點:
——該對象還沒有被持久化【沒有保存在數據庫中】
——不受Session的管理

User user = new User();
IdCard idCard = new IdCard();
// 這樣,對象處於臨時/瞬時狀態了

1.2 持久化狀態

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

session.save(idCard);
transaction.commit();
// 這樣,對象處於持久化狀態了。

1.3 遊離狀態

含義:當Session關閉了以後,持久化的對象就變成了遊離狀態了。
特點:
——在數據庫有對應的數據
——不處於session的管理

session.save(idCard);
transaction.commit();
session.close();
// 這樣,對象處於遊離狀態了。

二、一級緩存

2.1 含義

項目 含義
Hibenate中一級緩存 也叫做session的緩存。
目的 在session範圍內,減少對數據庫的訪問次數!從而提升hibernate的執行效率!
有效範圍 session。 Session關閉,一級緩存失效!

注意
1、持久化狀態的對象,都受Session管理,都會在Session緩存中!
2、Session的緩存由hibernate維護,用戶不能操作緩存內容。
3、 若想操作緩存內容,必須通過hibernate提供的evit/clear方法操作。

示例說明:
1、更新數據庫
已知:user表中(id = 1,username=“張珊”)。
目的:把id=1的username改爲“張珊”。

// 獲取id=1的對象信息。
User user = (User)session.get(User.class,1);
// 修改username爲“張珊”。
user.setUsername("張珊");
// 提交事務。
transaction.commit();
// 關閉session。
session.close();

結果:只有一條查詢SQL,並沒有更新SQL。
原因:查詢的結果被緩存了。修改後的數據與緩存的數據一樣,所以沒有執行更新操作。
2、獲取數據
已知:user表中(id = 1,username=“張珊”)。
目的:二次獲取該信息。

User user = null;
user = (User)session.get(User.class,1); // 一次獲取
user = (User)session.get(User.class,1);// 二次獲取
// 提交事務。
transaction.commit();
// 關閉session。
session.close();

結果:只有一條查詢SQL。
原因:一次查詢的結果被緩存了。 二次獲取直接從緩存中獲取。

2.2 緩存相關的方法

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

2.2.1 clear

 User user = null;
user = (User) session.get(User.class, 1);
//清除緩存,那麼下面獲取的時候,就不能從緩存裏面拿了
session.clear();
user = (User) session.get(User.class, 1);
// 結果:執行兩次查詢語句。

2.2.2 flush

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

User user = null;
user = (User) session.get(User.class, 1);
user.setUsername("李四");
user = (User) session.get(User.class, 1);
user.setUsername("王五");
// 結果:執行一次更新SQL語句。

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

User user = null;
user = (User) session.get(User.class, 1);
user.setUsername("李四");
session.flush();
user = (User) session.get(User.class, 1);
user.setUsername("王五");
// 結果:執行兩次更新SQL語句。

2.2.3 方法適用場景

一般地,我們在批處理的時候會用,因爲緩存也是有大小的。
思路:
——每隔一定記錄數,先與數據庫同步 flush()
——再清空緩存 clear()
注意:不同的Session是不會共享緩存的!

2.3 Iterator與list

使用HQL查詢全部數據的時候,可以使用list()得到所有的數據,也可以使用iterator()得到一個迭代器,再遍歷迭代器。
二者都可以獲取緩存的數據。

2.4 懶加載

含義:當使用數據的時候纔去獲取數據、執行對應的SQL語句…當還沒用到數據的時候,就不加載對應的數據!
作用:提高Hibernate的性能,提高執行效率!
——get: 及時加載,只要調用get方法立刻向數據庫查詢
——load:默認使用懶加載,當用到數據的時候才向數據庫查詢。

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

2.4.1 修改懶加載

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

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

2.4.2 懶加載異常

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

session.close();
System.out.println(user);
// 結果:報出異常。
異常解決方法 含義 示例
方式1 先使用一下數據 dept.getDeptName();
方式2 強迫代理對象初始化 Hibernate.initialize(dept);
方式3 關閉懶加載 設置lazy=false;
方式4 在使用數據之後,再關閉session!

三、二級緩存

3.1 含義

項目 含義
來源 Hibernate自帶
含義 基於應用程序的緩存
範圍 所有的Session都可以使用
存儲內容 存儲的是常用的類

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

3.2 配置二級緩存

二級緩存是Hibernate自帶的,可以在hibernate.properties文件中找到對應的信息。
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)指定哪些類加入二級緩存

3.2.1 開啓二級緩存

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

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

3.2.2 指定緩存框架

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

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

3.2.3 指定哪些類加入二級緩存

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

3.2.4 測試

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

public static void testCache2() {

	Session session1 = getSession();
	Transaction transaction = session1.getTransaction();
	transaction.begin();
	Monkey monkey = (Monkey) session1.get(Monkey.class,"40283f815be67f42015be67f43240001" );
	System.out.println(monkey.getName());
	
	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();
}

結果:只有一個查詢語句。輸出正確結果。說明二級緩存生效。

3.3 二級緩存策略

<!-- c. 指定哪一些類,需要加入二級緩存 -->
<class-cache usage="read-write" class="com.my.entity.Monkey"/>
<class-cache usage="read-only" class="com.my.entity.Cat"/>
屬性值 含義
read-only 放入二級緩存的對象,只讀;
read-write 放入二級緩存的對象,可以讀、寫;

非嚴格的讀寫
(基於事務的策略)

3.4 集合緩存

問題:在數據庫查詢的數據是集合時,Hibernate默認是沒有爲集合數據設置二級緩存的。因此,還需要去讀寫數據庫的信息。

3.4.1 集合配置二級緩存

在hibernate.cgf.xml中配置對象中的集合爲二級緩存。

<!-- 集合緩存[集合緩存的元素對象,也加加入二級緩存] -->
<collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>

3.4.2 測試

public static 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();
}

3.5 查詢緩存

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

1、默認的查詢數據是不放在二級緩存中的。
2、若想把查詢數據放到二級緩存,就需要在配置文件中開啓。

 <!-- 開啓查詢緩存 -->
<property name="hibernate.cache.use_query_cache">true</property>

在使用程序查詢的時候,也要調用setCacheable()方法,設置爲查詢緩存。

public static 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();
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章