ehcache二級緩存
⑴ java web寮鍙戠紦瀛樻柟妗堬紝ehcache鍜宺edis鍝涓鏇村ソ
浣跨敤鍦烘櫙涓嶄竴鏍 , 涓嶅瓨鍦ㄥ摢涓鏇村ソ, 鍙浠ュ幓寮婧愪腑鍥 鐪嬩笅 綰㈣柉鏁欎富鍐欑殑 :
Java涓ょ駭緙撳瓨妗嗘灦 J2Cache
J2Cache 鏄 OSChina 鐩鍓嶆e湪浣跨敤鐨勪袱綰х紦瀛樻嗘灦銆傜涓綰х紦瀛樹嬌鐢 Ehcache錛岀浜岀駭緙撳瓨浣跨敤 Redis 銆傜敱浜庡ぇ閲忕殑緙撳瓨璇誨彇浼氬艱嚧 L2 鐨勭綉緇滄垚涓烘暣涓緋葷粺鐨勭摱棰堬紝鍥犳 L1 鐨勭洰鏍囨槸闄嶄綆瀵 L2 鐨勮誨彇嬈℃暟銆傝ョ紦瀛樻嗘灦涓昏佺敤浜庨泦緹ょ幆澧冧腑銆傚崟鏈轟篃鍙浣跨敤錛岀敤浜庨伩鍏嶅簲鐢ㄩ噸鍚瀵艱嚧鐨 Ehcache 緙撳瓨鏁版嵁涓㈠け銆
⑵ hibernate怎麼實現緩存
一、why(為什麼要用Hibernate緩存?)
Hibernate是一個持久層框架,經常訪問物理資料庫。
為了降低應用程序對物理數據源訪問的頻次,從而提高應用程序的運行性能。
緩存內的數據是對物理數據源中的數據的復制,應用程序在運行時從緩存讀寫數據,在特定的時刻或事件會同步緩存和物理數據源的數據。
二、what(Hibernate緩存原理是怎樣的?)Hibernate緩存包括兩大類:Hibernate一級緩存和Hibernate二級緩存。
1.Hibernate一級緩存又稱為「Session的緩存」。
Session內置不能被卸載,Session的緩存是事務范圍的緩存(Session對象的生命周期通常對應一個資料庫事務或者一個應用事務)。
一級緩存中,持久化類的每個實例都具有唯一的OID。
2.Hibernate二級緩存又稱為「SessionFactory的緩存」。
由於SessionFactory對象的生命周期和應用程序的整個過程對應,因此Hibernate二級緩存是進程范圍或者集群范圍的緩存,有可能出現並發問題,因此需要採用適當的並發訪問策略,該策略為被緩存的數據提供了事務隔離級別。
第二級緩存是可選的,是一個可配置的插件,默認下SessionFactory不會啟用這個插件。
Hibernate提供了org.hibernate.cache.CacheProvider介面,它充當緩存插件與Hibernate之間的適配器。
什麼樣的數據適合存放到第二級緩存中?
1) 很少被修改的數據
2) 不是很重要的數據,允許出現偶爾並發的數據
3) 不會被並發訪問的數據
4) 常量數據
不適合存放到第二級緩存的數據?
1) 經常被修改的數據
2) 絕對不允許出現並發訪問的數據,如財務數據,絕對不允許出現並發
3) 與其他應用共享的數據。
3.Session的延遲載入實現要解決兩個問題:正常關閉連接和確保請求中訪問的是同一個session。
Hibernate session就是java.sql.Connection的一層高級封裝,一個session對應了一個Connection。
http請求結束後正確的關閉session(過濾器實現了session的正常關閉);延遲載入必須保證是同一個session(session綁定在ThreadLocal)。
4.Hibernate查找對象如何應用緩存?
當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;
查不到,如果配置了二級緩存,那麼從二級緩存中查;
如果都查不到,再查詢資料庫,把結果按照ID放入到緩存刪除、更新、增加數據的時候,同時更新緩存。
5.一級緩存與二級緩存的對比圖。
一級緩存
二級緩存
存放數據的形式
相互關聯的持久化對象
對象的散裝數據
緩存的范圍
事務范圍,每個事務都擁有單獨的一級緩存
進程范圍或集群范圍,緩存被同一個進程或集群范圍內所有事務共享
並發訪問策略
由於每個事務都擁有單獨的一級緩存不會出現並發問題,因此無須提供並發訪問策略
由於多個事務會同時訪問二級緩存中的相同數據,因此必須提供適當的並發訪問策略,來保證特定的事務隔離級別
數據過期策略
處於一級緩存中的對象永遠不會過期,除非應用程序顯示清空或者清空特定對象
必須提供數據過期策略,如基於內存的緩存中對象的最大數目,允許對象處於緩存中的最長時間,以及允許對象處於緩存中的最長空閑時間
物理介質
內存
內存和硬碟,對象的散裝數據首先存放到基於內存的緩存中,當內存中對象的數目達到數據過期策略的maxElementsInMemory值,就會把其餘的對象寫入基於硬碟的緩存中
緩存軟體實現
在Hibernate的Session的實現中包含
由第三方提供,Hibernate僅提供了緩存適配器,用於把特定的緩存插件集成到Hibernate中
啟用緩存的方式
只要通過Session介面來執行保存,更新,刪除,載入,查詢,Hibernate就會啟用一級緩存,對於批量操作,如不希望啟用一級緩存,直接通過JDBCAPI來執行
用戶可以再單個類或類的單個集合的粒度上配置第二級緩存,如果類的實例被經常讀,但很少被修改,就可以考慮使用二級緩存,只有為某個類或集合配置了二級緩存,Hibernate在運行時才會把它的實例加入到二級緩存中
用戶管理緩存的方式
一級緩存的物理介質為內存,由於內存的容量有限,必須通過恰當的檢索策略和檢索方式來限制載入對象的數目,Session的evit()方法可以顯示的清空緩存中特定對象,但不推薦
二級緩存的物理介質可以使內存和硬碟,因此第二級緩存可以存放大容量的數據,數據
過期策略的maxElementsInMemory屬性可以控制內存中的對象數目,管理二級緩存主要包括兩個方面:選擇需要使用第二級緩存的持久化類,設
置合適的並發訪問策略;選擇緩存適配器,設置合適的數據過期策略。SessionFactory的evit()方法也可以顯示的清空緩存中特定對象,但不
推薦
三、how(Hibernate的緩存機制如何應用?)
1. 一級緩存的管理:
evit(Object obj) 將指定的持久化對象從一級緩存中清除,釋放對象所佔用的內存資源,指定對象從持久化狀態變為脫管狀態,從而成為游離對象。
clear() 將一級緩存中的所有持久化對象清除,釋放其佔用的內存資源。
contains(Object obj) 判斷指定的對象是否存在於一級緩存中。
flush() 刷新一級緩存區的內容,使之與資料庫數據保持同步。
2.一級緩存應用: save()。當session對象調用save()方法保存一個對象後,該對象會被放入到session的緩存中。
get()和load()。當session對象調用get()或load()方法從資料庫取出一個對象後,該對象也會被放入到session的緩存中。
使用HQL和QBC等從資料庫中查詢數據。
public class Client
{
public static void main(String[] args)
{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try
{
/*開啟一個事務*/
tx = session.beginTransaction();
/*從資料庫中獲取id=""的Customer對象*/
Customer customer1 = (Customer)session.get(Customer.class, "");
System.out.println("customer.getUsername is"+customer1.getUsername());
/*事務提交*/
tx.commit();
System.out.println("-------------------------------------");
/*開啟一個新事務*/
tx = session.beginTransaction();
/*從資料庫中獲取id=""的Customer對象*/
Customer customer2 = (Customer)session.get(Customer.class, "");
System.out.println("customer2.getUsername is"+customer2.getUsername());
/*事務提交*/
tx.commit();
System.out.println("-------------------------------------");
/*比較兩個get()方法獲取的對象是否是同一個對象*/
System.out.println("customer1 == customer2 result is "+(customer1==customer2));
}
catch (Exception e)
{
if(tx!=null)
{
tx.rollback();
}
}
finally
{
session.close();
}
}
}
結果
Hibernate:
select
customer0_.id as id0_0_,
customer0_.username as username0_0_,
customer0_.balance as balance0_0_
from
customer customer0_
where
customer0_.id=?
customer.getUsername islisi
-------------------------------------
customer2.getUsername islisi
-------------------------------------
customer1 == customer2 result is true
輸出結果中只包含了一條SELECT SQL語句,而且customer1 == customer2 result is
true說明兩個取出來的對象是同一個對象。其原理是:第一次調用get()方法,
Hibernate先檢索緩存中是否有該查找對象,發現沒有,Hibernate發送SELECT語句到資料庫中取出相應的對象,然後將該對象放入緩存
中,以便下次使用,第二次調用get()方法,Hibernate先檢索緩存中是否有該查找對象,發現正好有該查找對象,就從緩存中取出來,不再去資料庫
中檢索。
3.二級緩存的管理:
evict(Class arg0, Serializable arg1)將某個類的指定ID的持久化對象從二級緩存中清除,釋放對象所佔用的資源。
sessionFactory.evict(Customer.class, new Integer(1));
evict(Class arg0) 將指定類的所有持久化對象從二級緩存中清除,釋放其佔用的內存資源。
sessionFactory.evict(Customer.class);
evictCollection(String arg0) 將指定類的所有持久化對象的指定集合從二級緩存中清除,釋放其佔用的內存資源。
sessionFactory.evictCollection("Customer.orders");
4.二級緩存的配置
常用的二級緩存插件
EHCache org.hibernate.cache.EhCacheProvider
OSCache org.hibernate.cache.OSCacheProvider
SwarmCahe org.hibernate.cache.SwarmCacheProvider
JBossCache org.hibernate.cache.TreeCacheProvider
<!-- EHCache的配置,hibernate.cfg.xml -->
<hibernate-configuration>
<session-factory>
<!-- 設置二級緩存插件EHCache的Provider類-->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- 啟動"查詢緩存" -->
<property name="hibernate.cache.use_query_cache">
true
</property>
</session-factory>
</hibernate-configuration>
<!-- ehcache.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--
緩存到硬碟的路徑
-->
<diskStore path="d:/ehcache"></diskStore>
<!--
默認設置
maxElementsInMemory : 在內存中最大緩存的對象數量。
eternal : 緩存的對象是否永遠不變。
timeToIdleSeconds :可以操作對象的時間。
timeToLiveSeconds :緩存中對象的生命周期,時間到後查詢數據會從資料庫中讀取。
overflowToDisk :內存滿了,是否要緩存到硬碟。
-->
<defaultCache maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
<!--
指定緩存的對象。
下面出現的的屬性覆蓋上面出現的,沒出現的繼承上面的。
-->
<cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
</ehcache>
<!-- *.hbm.xml -->
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class>
<!-- 設置該持久化類的二級緩存並發訪問策略 read-only read-write nonstrict-read-write transactional-->
<cache usage="read-write"/>
</class>
</hibernate-mapping>
若存在一對多的關系,想要在在獲取一方的時候將關聯的多方緩存起來,需要在集合屬性下添加<cache>子標簽,這里需要將關聯的對象的hbm文件中必須在存在<class>標簽下也添加<cache>標簽,不然Hibernate只會緩存OID。
<hibernate-mapping>
<class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">
<!-- 主鍵設置 -->
<id name="id" type="string">
<column name="id"></column>
<generator class="uuid"></generator>
</id>
<!-- 屬性設置 -->
<property name="username" column="username" type="string"></property>
<property name="balance" column="balance" type="integer"></property>
<set name="orders" inverse="true" cascade="all" lazy="false" fetch="join">
<cache usage="read-only"/>
<key column="customer_id" ></key>
<one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>
</set>
</class>
</hibernate-mapping>
⑶ encache.index鏂囦歡璁劇疆瀛樻斁璺寰
鍦ㄧ郴緇熸牴鐩褰曟湁涓 .FineReport110 鏂囦歡澶癸紝榪欎釜鐩褰曚笅鐨勬墍鏈夋枃浠墮兘鏄鏈嶅姟鍣ㄧ敓鎴愮殑銆
鍏朵腑鏈変釜 ./ehcache_disk_store 鏂囦歡錛岀粡甯稿崰鐢ㄩ潪甯稿ぇ鐨勭┖闂達紝瀵艱嚧鏍圭洰褰曞緢瀹規槗灝辨弧浜嗭紝閭d箞鎬庝箞淇鏀 ./ehcache_disk_store 瀛樻斁璺寰勫憿錛
1.2 瀹炵幇鎬濊礬
./ehcache_disk_store 閲屾斁鐨勬槸涓ょ嶇被鍨嬬殑緙撳瓨鏂囦歡錛屽垎鍒鏄錛氭暟鎹搴撲簩綰х紦瀛樸佹枃浠舵湇鍔″櫒鐨勭紦瀛
鎿嶄綔鏃跺苟涓嶆槸鐩存帴淇鏀 ./ehcache_disk_store 鐨勮礬寰勶紝鑰屾槸鍒嗗埆淇鏀硅繖涓ょ嶇紦瀛樻枃浠剁殑璺寰勩
2. 鎿嶄綔鏂規硶
2.1 淇鏀規暟鎹搴撲簩綰х紦瀛樿礬寰
鍦ㄥ伐紼嬬洰褰 WEB-INF/config 鏂囦歡澶逛笅鍒涘緩涓涓鍚嶄負 ehcache.db.properties 鐨勬枃浠躲
鍏朵腑璺熸暟鎹搴撲簩綰х紦瀛樿礬寰勭浉鍏崇殑閰嶇疆瑙佷笅鍥炬嗗嚭鏉ョ殑閮ㄥ垎錛屽彲鏍規嵁鍦烘櫙瀹為檯榪涜屼慨鏀廣
鏂囦歡閰嶇疆欏瑰備笅錛
# 鏁版嵁搴揺hcache閰嶇疆
# 鏄鍚︾佺敤浜岀駭緙撳瓨錛岄粯璁や笉紱佺敤false
disableCache=false
# 鍫嗕腑鏈澶х紦瀛樼┖闂達紝榛樿256M
maxBytesLocalHeap=1G
# 緙撳瓨鏄鍚﹀父椹伙紝true鏃秚imeToIdleSecond鍜宼imeToLiveSecond鏃犳晥錛岄粯璁false
eternal=false
# 浣跨敤鍚庣殑絀洪棽鏃墮棿錛岄粯璁120
timeToIdleSecond=120
# 鍒涘緩鍚庣殑瀛樻椿鏃墮棿錛岄粯璁120
timeToLiveSecond=120
# 緙撳瓨榪囨湡絳栫暐錛屽彇鍊糒RU錛孡FU錛孎IFO錛孋LOCK錛岄粯璁LRU
memoryStoreEvictionPolicy=LRU
# 綰跨▼媯鏌ョ紦瀛樿秴鏃剁殑闂撮殧鏃墮棿錛岄粯璁120
=120
# 鎸佷箙鍖栫被鍨嬶紝鍙栧糒OCALTEMPSWAP錛孌ISTRIBUTED錛孡OCALRESTARTABLE錛孨ONE錛岄粯璁NONE
persistenceType=LOCALTEMPSWAP
# 鎸囧畾鏁版嵁搴撲簩綰х紦瀛樿礬寰
diskStore=C:\\Users\\root\\Desktop\\tmp\\db_cache
# 鎸佷箙鍖栧悓姝ュ啓鍏ワ紝榛樿false
persistenceSynchronousWrites=false
# 姣忛殧涓孌墊椂闂存竻鐞嗚繃鏈熺紦瀛橈紝鍗曚綅縐掞紝榛樿60
recycleCacheMemoryInterval=60
# 姣忛殧涓孌墊椂闂磀ebug杈撳嚭緙撳瓨浣跨敤鎯呭喌錛堜粎鍦ㄨ皟璇曟椂浣跨敤錛屽悓鏃舵棩蹇楃駭鍒闇瑕佽皟鑷矰EBUG錛夛紝鍗曚綅縐掞紝涓0鏃朵笉寮鍚錛岄粯璁0
reportCacheStatusInterval=0
2.2 淇鏀規枃浠舵湇鍔″櫒緙撳瓨璺寰
鍦 FineDB 鐨 fine_conf_entity 琛ㄤ腑鏂板炰互涓嬩袱涓瀛楁點傛柊澧炲瓧孌墊椂璇風洿鎺ヨ繛鎺ュ埌 FineDB 鍦ㄨ〃閲屾墜鍔ㄤ慨鏀癸紝涓嶆敮鎸佷嬌鐢 FINE_CONF_ENTITY鍙瑙嗗寲閰嶇疆鎻掍歡 閰嶇疆銆
FineDB 鏁版嵁搴撶被鍨嬪拰榪炴帴鏂規硶鍙傝佹枃妗o細FineDB鏁版嵁搴撶畝浠
瀛楁 鍊
ResourceCacheConfig.persistenceStrategy LOCALTEMPSWAP
ResourceCacheManagerConfig.diskStorePath diskStore=C:\\Users\\root\\Desktop\\tmp\\resource_cache
⑷ spring3.2.2+hibernate 怎麼集成ehcache
spring boot集成ehcache 2.x 用於hibernate二級緩存
spring boot集成ehcache 2x 用於hibernate二級緩存
項目依賴
Ehcache簡介
hibernate二級緩存配置
ehcache配置文件
ehcache事件監聽
註解方式使用二級緩存
完整代碼
本文將介紹如何在spring boot中集成ehcache作為hibernate的二級緩存。各個框架版本如下
spring boot:1.4.3.RELEASE
spring framework: 4.3.5.RELEASE
hibernate:5.0.1.Final(spring-boot-starter-data-jpa默認依賴)
ehcache:2.10.3
項目依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<exclusions>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.3</version>
</dependency>
Ehcache簡介
ehcache是一個純Java的緩存框架,既可以當做一個通用緩存使用,也可以作為將其作為hibernate的二級緩存使用。緩存數據可選擇如下三種存儲方案
MemoryStore – On-heap memory used to hold cache elements. This tier is subject to Java garbage collection.
OffHeapStore – Provides overflow capacity to the MemoryStore. Limited in size only by available RAM. Not subject to Java garbage collection (GC). Available only with Terracotta BigMemory procts.
DiskStore – Backs up in-memory cache elements and provides overflow capacity to the other tiers.
hibernate二級緩存配置
hibernate的二級緩存支持entity和query層面的緩存,org.hibernate.cache.spi.RegionFactory各類可插拔的緩存提供商與hibernate的集成。
# 打開hibernate統計信息
spring.jpa.properties.hibernate.generate_statistics=true
# 打開二級緩存
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
# 打開查詢緩存
spring.jpa.properties.hibernate.cache.use_query_cache=true
# 指定緩存provider
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
# 配置shared-cache-mode
spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
關於hibernate緩存相關的所有配置可參考hibernate5.0官方文檔#緩存
ehcache配置文件
ehcache 2.x配置文件樣板參考官方網站提供的ehcache.xml。本例中使用的配置文件如下所示
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<diskStore path="user.dir/cache"/>
<transactionManagerLookup class="net.sf.ehcache.transaction.manager."
properties="jndiName=java:/TransactionManager" propertySeparator=";"/>
< class="com.yangyi.base.ehcache." properties=""/>
<defaultCache
maxEntriesLocalHeap="0"
eternal="false"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200">
<!--<terracotta/>-->
</defaultCache>
<cache name="entityCache"
maxEntriesLocalHeap="1000"
maxEntriesLocalDisk="10000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="10"
timeToLiveSeconds="20"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap"/>
<cacheEventListenerFactory class="com.yangyi.base.ehcache." />
</cache>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
<persistence strategy="localTempSwap" />
<cacheEventListenerFactory class="com.yangyi.base.ehcache." />
</cache>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxEntriesLocalHeap="5000" eternal="true">
<persistence strategy="localTempSwap" />
<cacheEventListenerFactory class="com.yangyi.base.ehcache." />
</cache>
</ehcache>
註解方式使用二級緩存
要使用entity cache,需要在entity上配上相應的註解方可生效。javax.persistence.Cacheable註解標記該entity使用二級緩存,org.hibernate.annotations.Cache註解指定緩存策略,以及存放到哪個緩存區域。
有關緩存策略詳細信息可參考hibernate5.0官方文檔#緩存
package com.yangyi.entity;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.JoinTable;
@Entity
@Table(name = "users")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "entityCache")
public class User implements Serializable {
}
最後,我們需要在spring boot應用層面打開cache功能,使用org.springframework.cache.annotation.EnableCaching註解
package com.yangyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
完整代碼
完整代碼示例見github spring-boot-ehcache2.x-hibernate-second-level-cache
⑸ 關於Hibernate二級緩存的問題
<property
name="hibernate.cache.use_second_level_cache">true</property>
<property
name="hibernate.cache.provider_class">org.hibernate.cache.ehcacheprovider</property>
前一句是打開二級緩存,後一句是啟用第三方緩存產品(可改變,上面eache是hibernate官方默認的第三方緩存產品)
緩存是否實用,要看你的需求,
如果你的系統,瀏覽的人數比較多,但是增刪的比較少,緩存的功勞非常大
如果你的系統即時性非常強,那麼緩存的命中率就比較低,同時更新數據時,hibernate需要額外提供資源維護緩存與數據的一致
⑹ hibernate的延時載入與二級緩存具體怎麼解釋
延遲載入 意思是僅當應用系統需要訪問關聯的目標對象的數據時,hibernate才載入他們的值。就是只在一個對象調用他的「一對多」或「多對多」關聯時的目標對象才將目標對象讀取出來。 利用hibernate.initialize(),在關閉session後取得目標實體,則在使用org.hibernate包中hibernate類中靜態關系hibernate.initialize()來先載入關聯目標實體,再關閉session
因為session生命周期短,所以在第一級緩存的命中率實際應用時將是很低的。因此為提高hibernate框架時應用系統總體性能,開發者要配置二級緩存。
二級緩存就是sessionfactory級別的全局緩存,分為內置緩存和外置緩存。
配置方法嘛,在hibernate.cfg.xml中加入
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
EHCache的配置文件ehcache.xml和hibernate.cfg.xml放在一起,示例
<ehcache>
<diskStore path="java.eo.tmpdir">
<defaultCache maxElementsInMemory="1000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120"
overflowToDisk="true"/>
</ehcache>
雖然敲了這么多字,但是想完全弄清楚還是很困難的,畢竟書中是用專門的兩章內容講解的。建議去找本hibernate的書去看看,我手裡有本《J2EE項目——hibernate框架技術》,還不錯,樓主可以參考一下。
⑺ 什麼是hibernate中的二級緩存
第一級別的緩存是Session級別的緩存,是屬於事務范圍的緩存,由Hibernate管理,一般無需進行干預。第二級別的緩存是SessionFactory級別的緩存,是屬於進程范圍的緩存。
二級緩存也分為了兩種
內置緩存:Hibernate自帶的,不可卸載,通常在Hibernate的初始化階段,Hibernate會把映射元數據和預定義的SQL語句放置到SessionFactory的緩存中。該內置緩存是只讀的。
外置緩存:通常說的二級緩存也就是外置緩存,在默認情況下SessionFactory不會啟用這個緩存插件,外置緩存中的數據是資料庫數據的復制,外置緩存的物理介質可以是內存或者硬碟。
hibernate二級緩存的結構
2.並發訪問策略
transactional
(事務型)
僅在受管理的環境中適用
提供Repeatable Read事務隔離級別
適用經常被讀,很少修改的數據
可以防止臟讀和不可重復讀的並發問題
緩存支持事務,發生異常的時候,緩存也能夠回滾
read-write
(讀寫型)
提供Read Committed事務隔離級別
在非集群的環境中適用
適用經常被讀,很少修改的數據
可以防止臟讀
更新緩存的時候會鎖定緩存中的數據
nonstrict-read-write
(非嚴格讀寫型)
適用極少被修改,偶爾允許臟讀的數據(兩個事務同時修改數據的情況很少見)
不保證緩存和資料庫中數據的一致性
為緩存數據設置很短的過期時間,從而盡量避免臟讀
不鎖定緩存中的數據
read-only
(只讀型)
適用從來不會被修改的數據(如參考數據)
在此模式下,如果對數據進行更新操作,會有異常
事務隔離級別低,並發性能高
在集群環境中也能完美運作
分析:通過上述表格分析如下
適合放入二級緩存中數據
很少被修改
不是很重要的數據,允許出現偶爾的並發問題
不適合放入二級緩存中的數據
經常被修改
財務數據,絕對不允許出現並發問題
與其他應用數據共享的數據
⑻ 在配置Hibernate二級緩存的時候,在ehcache.xml中 有這么一句 <diskStore path="D:\cache"/>
你的理解基本上是正確的,但還要結合overflowToDisk參數來用。
另外cache文件不需要手動創建。
下面是一些說明:
由於配置文件中只能指定maxElementsInMemory,這就會有可能存入的對象太多而超出VM的heap大小,當然你可以通過jvm參數增大heap大小,但這總還是有可能溢出。這里可以把maxElementsInMemory值設置到一個比較安全的大小,自己預先測試一下最好。如果內存仍然存不下你需要存的對象個數,那麼可以開啟overflowToDisk來增加可以存儲的Element個數。這里要注意一下,EHCache不會自動幫助你去把內存對象寫入到磁碟,當超過maxElementsInMemory程序會自動把更多的部分開始往硬碟寫,但是內存的對象其實並沒有清出去,這時需要手動使用Cache.flush()方法來把內存對象
重建上一次運行的緩存:這個需求肯定比較普遍,我們當然不希望一旦程序退出,整個緩存就要重建了。開啟diskPersistent功能,只要使用的是CacheManager單例模式,下一次啟動的時候就會調用上一次運行的緩存。比較麻煩的是寫入磁碟的時間還是要自己調用Cache.flush()方法。如果僅僅考慮到程序重啟的話,我建議這里把diskStore寫入到一個ramfs,這樣性能就更高了,但重啟電腦的話就不得不重建緩存了。
⑼ 什麼事Hibernate二級緩存
只需要查一次資料庫,在次操作數據不用去查詢了,它會首先找緩存,看看有沒有,如果有就直接用,沒有的時候在去查!
⑽ hibernate緩存的詳細配置
很多人對二級緩存都不太了解,或者是有錯誤的認識,我一直想寫一篇文章介紹一下hibernate的二級緩存的,今天終於忍不住了。
我的經驗主要來自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請原諒我的頑固不化。
hibernate的session提供了一級緩存,每個session,對同一個id進行兩次load,不會發送兩條sql給資料庫,但是session關閉的時候,一級緩存就失效了。
二級緩存是SessionFactory級別的全局緩存,它底下可以使用不同的緩存類庫,比如ehcache、oscache等,需要設置hibernate.cache.provider_class,我們這里用ehcache,在2.1中就是
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查詢緩存,加上
hibernate.cache.use_query_cache=true
緩存可以簡單的看成一個Map,通過key在緩存裡面找value。
Class的緩存
對於一條記錄,也就是一個PO來說,是根據ID來找的,緩存的key就是ID,value是POJO。無論list,load還是iterate,只要讀出一個對象,都會填充緩存。但是list不會使用緩存,而iterate會先取資料庫select id出來,然後一個id一個id的load,如果在緩存裡面有,就從緩存取,沒有的話就去資料庫load。假設是讀寫緩存,需要設置:
<cache usage="read-write"/>
如果你使用的二級緩存實現是ehcache的話,需要配置ehcache.xml
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
其中eternal表示緩存是不是永遠不超時,timeToLiveSeconds是緩存中每個元素(這里也就是一個POJO)的超時時間,如果eternal="false",超過指定的時間,這個元素就被移走了。timeToIdleSeconds是發呆時間,是可選的。當往緩存裡面put的元素超過500個時,如果overflowToDisk="true",就會把緩存中的部分數據保存在硬碟上的臨時文件裡面。
每個需要緩存的class都要這樣配置。如果你沒有配置,hibernate會在啟動的時候警告你,然後使用defaultCache的配置,這樣多個class會共享一個配置。
當某個ID通過hibernate修改時,hibernate會知道,於是移除緩存。
這樣大家可能會想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到緩存了。實際上這是很難的,因為你無法判斷什麼時候是第一次,而且每次查詢的條件通常是不一樣的,假如資料庫裡面有100條記錄,id從1到100,第一次list的時候出了前50個id,第二次iterate的時候卻查詢到30至70號id,那麼30-50是從緩存裡面取的,51到70是從資料庫取的,共發送1+20條sql。所以我一直認為iterate沒有什麼用,總是會有1+N的問題。
(題外話:有說法說大型查詢用list會把整個結果集裝入內存,很慢,而iterate只select id比較好,但是大型查詢總是要分頁查的,誰也不會真的把整個結果集裝進來,假如一頁20條的話,iterate共需要執行21條語句,list雖然選擇若干欄位,比iterate第一條select id語句慢一些,但只有一條語句,不裝入整個結果集hibernate還會根據資料庫方言做優化,比如使用mysql的limit,整體看來應該還是list快。)
如果想要對list或者iterate查詢的結果緩存,就要用到查詢緩存了
查詢緩存
首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以後不是net.sf的包名了
<cache name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="7200" overflowToDisk="true"/>
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
然後
query.setCacheable(true);//激活查詢緩存
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選
第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個查詢緩存做一個單獨的配置,使用setCacheRegion來做這個指定,需要在ehcache.xml裡面配置它:
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
如果省略第二行,不設置cacheRegion的話,那麼會使用上面提到的標准查詢緩存的配置,也就是net.sf.hibernate.cache.StandardQueryCache
對於查詢緩存來說,緩存的key是根據hql生成的sql,再加上參數,分頁等信息(可以通過日誌輸出看到,不過它的輸出不是很可讀,最好改一下它的代碼)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
參數是"tiger%",那麼查詢緩存的key*大約*是這樣的字元串(我是憑記憶寫的,並不精確,不過看了也該明白了):
select * from cat c where c.name like ? , parameter:tiger%
這樣,保證了同樣的查詢、同樣的參數等條件下具有一樣的key。
現在說說緩存的value,如果是list方式的話,value在這里並不是整個結果集,而是查詢出來的這一串ID。也就是說,不管是list方法還是iterate方法,第一次查詢的時候,它們的查詢方式很它們平時的方式是一樣的,list執行一條sql,iterate執行1+N條,多出來的行為是它們填充了緩存。但是到同樣條件第二次查詢的時候,就都和iterate的行為一樣了,根據緩存的key去緩存裡面查到了value,value是一串id,然後在到class的緩存裡面去一個一個的load出來。這樣做是為了節約內存。
可以看出來,查詢緩存需要打開相關類的class緩存。list和iterate方法第一次執行的時候,都是既填充查詢緩存又填充class緩存的。
這里還有一個很容易被忽視的重要問題,即打開查詢緩存以後,即使是list方法也可能遇到1+N的問題!相同條件第一次list的時候,因為查詢緩存中找不到,不管class緩存是否存在數據,總是發送一條sql語句到資料庫獲取全部數據,然後填充查詢緩存和class緩存。但是第二次執行的時候,問題就來了,如果你的class緩存的超時時間比較短,現在class緩存都超時了,但是查詢緩存還在,那麼list方法在獲取id串以後,將會一個一個去資料庫load!因此,class緩存的超時時間一定不能短於查詢緩存設置的超時時間!如果還設置了發呆時間的話,保證class緩存的發呆時間也大於查詢的緩存的生存時間。這里還有其他情況,比如class緩存被程序強制evict了,這種情況就請自己注意了。
另外,如果hql查詢包含select字句,那麼查詢緩存裡面的value就是整個結果集了。
當hibernate更新資料庫的時候,它怎麼知道更新哪些查詢緩存呢?
hibernate在一個地方維護每個表的最後更新時間,其實也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置裡面。
當通過hibernate更新的時候,hibernate會知道這次更新影響了哪些表。然後它更新這些表的最後更新時間。每個緩存都有一個生成時間和這個緩存所查詢的表,當hibernate查詢一個緩存是否存在的時候,如果緩存存在,它還要取出緩存的生成時間和這個緩存所查詢的表,然後去查找這些表的最後更新時間,如果有一個表在生成時間後更新過了,那麼這個緩存是無效的。
可以看出,只要更新過一個表,那麼凡是涉及到這個表的查詢緩存就失效了,因此查詢緩存的命中率可能會比較低。
Collection緩存
需要在hbm的collection裡面設置
<cache usage="read-write"/>
假如class是Cat,collection叫children,那麼ehcache裡面配置
<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />
Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會因為這個表更新過就失效,一個collection緩存僅在這個collection裡面的元素有增刪時才失效。
這樣有一個問題,如果你的collection是根據某個欄位排序的,當其中一個元素更新了該欄位時,導致順序改變時,collection緩存裡面的順序沒有做更新。
緩存策略
只讀緩存(read-only):沒有什麼好說的
讀/寫緩存(read-write):程序可能要的更新數據
不嚴格的讀/寫緩存(nonstrict-read-write):需要更新數據,但是兩個事務更新同一條記錄的可能性很小,性能比讀寫緩存好
事務緩存(transactional):緩存支持事務,發生異常的時候,緩存也能夠回滾,只支持jta環境,這個我沒有怎麼研究過
讀寫緩存和不嚴格讀寫緩存在實現上的區別在於,讀寫緩存更新緩存的時候會把緩存裡面的數據換成一個鎖,其他事務如果去取相應的緩存數據,發現被鎖住了,然後就直接取資料庫查詢。
在hibernate2.1的ehcache實現中,如果鎖住部分緩存的事務發生了異常,那麼緩存會一直被鎖住,直到60秒後超時。
不嚴格讀寫緩存不鎖定緩存中的數據。
使用二級緩存的前置條件
你的hibernate程序對資料庫有獨占的寫訪問權,其他的進程更新了資料庫,hibernate是不可能知道的。你操作資料庫必需直接通過hibernate,如果你調用存儲過程,或者自己使用jdbc更新資料庫,hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級緩存的,但是據說3.1已經解決了這個問題。
這個限制相當的棘手,有時候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來優化,很郁悶吧。
SessionFactory也提供了移除緩存的方法,你一定要自己寫一些JDBC的話,可以調用這些方法移除緩存,這些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不過我不建議這樣做,因為這樣很難維護。比如你現在用JDBC批量更新了某個表,有3個查詢緩存會用到這個表,用evictQueries(String cacheRegion)移除了3個查詢緩存,然後用evict(Class persistentClass)移除了class緩存,看上去好像完整了。不過哪天你添加了一個相關查詢緩存,可能會忘記更新這里的移除代碼。如果你的jdbc代碼到處都是,在你添加一個查詢緩存的時候,還知道其他什麼地方也要做相應的改動嗎?
----------------------------------------------------
總結:
不要想當然的以為緩存一定能提高性能,僅僅在你能夠駕馭它並且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會大大的降低更新性能。在不了解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出臟數據。
如果受不了hibernate的諸多限制,那麼還是自己在應用程序的層面上做緩存吧。
在越高的層面上做緩存,效果就會越好。就好像盡管磁碟有緩存,資料庫還是要實現自己的緩存,盡管資料庫有緩存,咱們的應用程序還是要做緩存。因為底層的緩存它並不知道高層要用這些數據干什麼,只能做的比較通用,而高層可以有針對性的實現緩存,所以在更高的級別上做緩存,效果也要好些吧。