文章目錄
一、對象狀態
參考: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();
}