hibernate物件狀態與一級快取二級快取
1 hibernate 中 持久類物件狀態介紹
1.1hibernate 規定三種狀態:瞬時態、持久態、脫管態
瞬時態:transient, session沒有快取物件,資料庫也沒有對應記錄。
OID特點:沒有值
持久態:persistent, session快取物件,資料庫最終會有記錄。(事務沒有提交)
OID特點:有值
脫管態:detached, session沒有快取物件,資料庫有記錄。
OID特點:有值
1.2 狀態之間的轉換
1.3 瞬時態/臨時態
- 獲得:一般都是直接建立(new
- 瞬時態---->持久態
一般操作: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(); } |
- 問題:一級快取什麼時候重新整理?(瞭解)
預設情況提交(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 什麼是二級快取
- hibernate 提供快取機制:一級快取、二級快取
一級快取:session級別快取,在一次請求中共享資料。
二級快取:sessionFactory級別快取,整個應用程式共享一個會話工廠,共享一個二級快取。
- SessionFactory的快取兩部分: 內建快取:使用一個Map,用於存放配置資訊,預定義HQL語句等,提供給Hibernate框架自己使用,對外只讀的。不能操作。
2 外接快取:使用另一個Map,用於存放使用者自定義資料。預設不開啟。外接快取hibernate只提供規範(介面),需要第三方實現類。外接快取有成為二級快取。
3.2 二級快取內部結構
二級就是由4部分構成
類級別快取
集合級別快取
時間戳快取
查詢快取(二級快取的第2大部分,三級快取)
3.3 併發訪問策略
- 訪問策略:讀寫型(read-write)、只讀型(read-only)
3.4 應用場景
- 適合放入二級快取中的資料:
很少被修改
不是很重要的資料, 允許出現偶爾的併發問題
2.不適合放入二級快取中的資料:
經常被修改
財務資料, 絕對不允許出現併發問題
與其他應用資料共享的資料
3.5 二級快取提供商
- EHCache: 可作為程序(單機)範圍內的快取, 存放資料的物理介質可以是記憶體或硬碟, 對 Hibernate 的查詢快取提供了支援。--支援叢集。
- OpenSymphony `:可作為程序範圍內的快取, 存放資料的物理介質可以是記憶體或硬碟, 提供了豐富的快取資料過期策略, 對 Hibernate 的查詢快取提供了支援
- SwarmCache: 可作為叢集範圍內的快取, 但不支援 Hibernate 的查詢快取
- JBossCache:可作為叢集範圍內的快取, 支援 Hibernate 的查詢快取
X表示支援
3.6 ehcache配置二級快取
1.匯入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar
2.開啟二級快取(我要使用二級快取)
- 在hibernate.cfg.xml 配置二級快取
<!-- 9.1 開啟二級快取 --> <property name="hibernate.cache.use_second_level_cache">true</property> |
3.確定二級快取提供商(我要使用哪個二級快取)
- 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 類快取
- 類快取:只存放資料
- 一級快取:存放物件本身
@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"/>--> // *** 多了10條select,通過訂單的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 時間戳
- 時間戳:任何操作都在時間戳中記錄操作時間。
@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 查詢快取
- 查詢快取又稱為三級快取(民間)
- 查詢快取預設不使用。需要手動開啟
- 查詢快取:將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配置檔案
- <diskStore path="java.io.tmpdir"/> 設定臨時檔案存放位置。(快取一般記憶體,一定程度時,寫入硬碟。)
- 快取詳細設定
<defaultCache> 所有的快取物件預設的配置
<cache name="類"> 指定物件單獨配置
- 引數設定
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)
|