在看hibernate的官方文档时,看到关于缓存的介绍。
hibernate在缓存管理上做的很好,具体使用,本章不做讲解,本篇主要研究EhCache的用法。其中hibernate使用到的缓存提供商列表如下:
Cache | Provider class | Type | Cluster Safe | Query Cache Supported |
---|---|---|---|---|
Hashtable (not intended for production use) | org.hibernate.cache.HashtableCacheProvider | memory | yes | |
EHCache | org.hibernate.cache.EhCacheProvider | memory, disk, transactional, clustered | yes | yes |
OSCache | org.hibernate.cache.OSCacheProvider | memory,disk | yes | |
SwarmCache | org.hibernate.cache.SwarmCacheProvider | clustered (ip multicast) | yes (clustered invalidation) | |
JBoss Cache 1.x | org.hibernate.cache.TreeCacheProvider | clustered (ip multicast), transactional | yes (replication) | yes (clock sync req.) |
JBoss Cache 2 | org.hibernate.cache.jbc.JBossCacheRegionFactory | clustered (ip multicast), transactional | yes (replication or invalidation) | yes (clock sync req.) |
其中,我对EHCache比较感兴趣。看它支持的类型包括对内存,硬盘,传统,集群都支持。
我们可以单独研究一下Ehcache缓存的使用,这样方便以后我们对其他使用到缓存的地方进行缓存的自定义管理(不单单在hibernate查询数据方面)。
ehcache下载地址: http://sourceforge.net/projects/ehcache/files/ehcache/
先写个例子,看看它的api如何使用:
EhcacheTest
package org.base.cache.test;
import java.net.MalformedURLException;
import java.net.URL;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;
/**
* Ehcache缓存管理的api测试小例子
* @author lushuaiyin
*
*/
public class EhcacheTest {
/**
* @param args
*/
public static void main(String[] args) throws MalformedURLException {
net.sf.ehcache.config.Configuration config=new Configuration();
//如果不使用ehcache.xml配置文件,那么必须用代码配置一个defaultCacheConfiguration
CacheConfiguration defaultCacheConfiguration=new CacheConfiguration();
defaultCacheConfiguration.setMaxEntriesLocalHeap(0);
defaultCacheConfiguration.setEternal(false);
defaultCacheConfiguration.setTimeToIdleSeconds(30);
defaultCacheConfiguration.setTimeToLiveSeconds(30);
config.addDefaultCache(defaultCacheConfiguration);//设置默认cache
net.sf.ehcache.CacheManager cacheManager=CacheManager.create(config);
//创建缓存信息
/*构造方法有多种,详见文档
public Cache(String name,
int maxElementsInMemory,
boolean overflowToDisk,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds)
*/
//自定义配置缓存
net.sf.ehcache.Cache cache1=new Cache("mycache-one", 1000, false, false, 30, 30);
cacheManager.addCache(cache1);
//只有配置了defaultCacheConfiguration,这个方法才可以使用。因为用字符串命名的缓存必须有实际配置。
cacheManager.addCache("mycache-two");//添加一个空缓存
//往缓存中放值
String objkey1="key1",objvalue1="value1";
cache1.put(new Element(objkey1,objvalue1));//直接放
//遍历取出某个缓存中的所有值
if(cacheManager.getCache("mycache-one")!=null){
Cache cache11=cacheManager.getCache("mycache-one");
if(cache11.getKeys().size()==0){
System.out.println("mycache-one exits,but no value.");
}else{
for(int i=0;i<cache11.getKeys().size();i++){
Object thekey=cache11.getKeys().get(i);
Object thevalue=cache11.get(thekey);
System.out.println("mycache-one-"+i+",key:"+thekey.toString()+",value:"+thevalue.toString());
}
}
}else{
System.out.println("mycache-one-is null");
}
/*打印
mycache-one-0,key:key1,value:[ key = key1, value=value1, version=1, hitCount=1, CreationTime = 1366263629054, LastAccessTime = 1366263629054 ]
*/
if(cacheManager.getCache("mycache-two")!=null){
Cache cache2=cacheManager.getCache("mycache-two");
if(cache2.getKeys().size()==0){
System.out.println("mycache-two exits,but no value.");
}else{
for(int i=0;i<cache2.getKeys().size();i++){
Object thekey=cache2.getKeys().get(i);
Object thevalue=cache2.get(thekey);
System.out.println("mycache-two-"+i+",key:"+thekey.toString()+",value:"+thevalue.toString());
}
}
}else{
System.out.println("mycache-two-is null");
}
/*打印
mycache-two exits,but no value.
*/
}
}
Ehcache在使用的大多数情况,是用ehcache.xml来配置的。在spring中的集成很方便。
下面我们使用ehcache.xml,但不在web环境下,对缓存进行自定义。
org/base/cache/test/myehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir"/>
<!-- JTA事务配置。class属性若为空,则默认会按照一个顺序寻找TransactionManager对象。
也可以自定义,需要实现接口net.sf.ehcache.transaction.manager.TransactionManagerLookup
-->
<!--
<transactionManagerLookup class="net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup"
properties="jndiName=java:/TransactionManager" propertySeparator=";"/>
-->
<!-- CacheManagerEventListener 缓存监听,根据需要自定义监听类
<cacheManagerEventListenerFactory class="" properties=""/>
-->
<!-- Terracotta服务器集群配置,详细看文档 -->
<!--
<terracottaConfig url="localhost:9510"/>
-->
<defaultCache
maxEntriesLocalHeap="0"
eternal="false"
timeToIdleSeconds="30"
timeToLiveSeconds="30">
<!-- <terracotta/>-->
</defaultCache>
<!--
缓存名为myCache1,
这个缓存最多包含10000个元素在内存中,并将
闲置超过5分钟和存在超过10分钟的元素释放。
如果超过10000元素,将溢流到磁盘缓存,并且硬盘缓存最大数量是1000.
硬盘路径是定义的java.io.tmp。
-->
<cache name="myCache1"
maxEntriesLocalHeap="500"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap"/>
</cache>
<!--
缓存名为sampleCache2。
此缓存在内存中最大元素的数量是1000。
没有设置溢出到磁盘,所以1000就是这个缓存的最大值。
注意,当一个缓存eternal设置成true,那么TimeToLive
和timeToIdle江不起作用。
<cache name="sampleCache2"
maxEntriesLocalHeap="1000"
eternal="true"
memoryStoreEvictionPolicy="FIFO"/>
-->
<!--
缓存名为sampleCache3的。
这个缓存溢出会到磁盘。磁盘缓存存储在虚拟机重新启动前会持久有效。
磁盘的终止线程的时间间隔设置为3分钟,覆盖默认的2分钟。
<cache name="sampleCache3"
maxEntriesLocalHeap="500"
eternal="false"
overflowToDisk="true"
diskPersistent="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskExpiryThreadIntervalSeconds="180"
memoryStoreEvictionPolicy="LFU">
</cache>
-->
<!--
Terracotta集群缓存sampleTerracottaCache。
<cache name="sampleTerracottaCache"
maxBytesLocalHeap="10m"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="1800">
<terracotta/>
</cache>
-->
</ehcache>
关于配置的属性的含义,可以到官网的文档中查看,这里给出一些常用的属性。
Cache的以下属性是必须的。
name:
cache的唯一标识。
maxEntriesLocalHeap:
在内存创建对象的最大数量。0=无限制。
无限制实际指Integer.MAX_SIZE (2147483647)。
maxEntriesLocalDisk:
设置在硬盘上存储的对象的最大数量。默认0,即无限制。
eternal:
设置元素是否持久化。如果是,元素不会过期。
Cache的以下属性是可选的。
overflowToOffHeap:
此功能仅在企业版的Ehcache。
当设置为true,可利用无限制的离堆内存的缓存
存储,以提高性能。离堆内存是不受Java
GC限制的。默认值是false。
maxBytesLocalHeap:
定义多少字节缓存可能会使用虚拟机的堆。如果一个CacheManager的
maxBytesLocalHeap已经被定义,这个缓存的指定金额将
减去从CacheManager的。其他的高速缓存将分享剩下的人。
此属性的值是<数字> K | K |米| M| G| G
千字节(K| K),兆字节(M| M)或千兆字节(G| G)。
例如,maxBytesLocalHeap的“2G”下发2 GB的堆内存。
如果您指定一个maxBytesLocalHeap,就不能再使用属性maxEntriesLocalHeap。
maxBytesLocalOffHeap:
此功能仅在企业版的Ehcache。
离堆内存量,可以使用这个缓存设置,将保留。
此设置将设置overflowToOffHeap为true 。设置explicitly为false来禁用溢出行为。
需要注意的是使用时离堆,设置maxEntriesLocalHeap建议至少100个元素,
否则性能会出现严重退化,并提出警告。
可分配的最低金额为128MB。没有最大值。
maxBytesLocalDisk:
As for maxBytesLocalHeap, but specifies the limit of disk storage this cache will ever use.
timeToIdleSeconds:
设置元素闲置时长。单位:秒。(在eternal设置成false的情况下有效)
可选属性。值为0意味着元素可以闲置无穷。
默认值是0。
timeToLiveSeconds:
设置元素过期时长。单位:秒。(在eternal设置成false的情况下有效)
可选属性。值为0意味着,元素可以住无穷。
默认值是0。
diskExpiryThreadIntervalSeconds:
磁盘到期线程运行之间的秒数。默认值为120秒。
diskSpoolBufferSizeMB:
这是分配硬盘存储的缓冲区的大小。信息被写入
这个区域,然后异步写入到磁盘中。默认大小为30MB。
每个缓冲区仅用于由其缓存。如果你遇到内存溢出错误试着
降低此值。为了提高硬盘存储性能应考虑增加此值。
clearOnFlush:
调用flush()方法时,硬盘存储缓存被清除。
默认值是true。
memoryStoreEvictionPolicy:
内存管理策略,默认是最近最少使用策略(即Least Recently Used,LRU)。
其他可选的有先进先出策略(即 First In First Out,FIFO),最少使用频率策略
(即Less Frequently Used,LFU)。
copyOnRead:
一个元素被复制时是否从缓存中读取。
默认false。
copyOnWrite:
一个元素被添加到缓存中时是否被复制。
默认false。
EhcacheManagerTest
package org.base.cache.test;
import java.net.URL;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
/**
* Ehcache缓存管理的初步学习实例
* @author lushuaiyin
*
*/
public class EhcacheManagerTest {
public static net.sf.ehcache.CacheManager cacheManager = null;
private static String configPath="org/base/cache/test/myehcache.xml";//配置文件路径,一般会放在源文件夹
private static String CACHE_MYCACHE1="myCache1";//定义文件中配置的缓存
//实例化cacheManager,单例模式
public static CacheManager getCacheManagerInstance(){
if (cacheManager == null) {
URL configUrl=null;
configUrl = EhcacheManagerTest.class.getClassLoader().getResource(configPath);
cacheManager = CacheManager.create(configUrl);
}
return cacheManager;
}
public static net.sf.ehcache.CacheManager getCacheManager() {
return getCacheManagerInstance();//单例缓存管理
}
//这个set可以不开放
public static void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
EhcacheManagerTest.cacheManager = cacheManager;
}
//添加新缓存
public static void addCacheByName(String cacheName){
if(cacheName==null||cacheName.trim().equals("")){
System.out.println("cacheName is null");
}else{
if(getCacheManager().getCache(cacheName.trim())!=null){
getCacheManager().removeCache(cacheName.trim());
}
getCacheManager().addCache(cacheName.trim());
System.out.println(cacheName+ "重新添加");
}
}
//得到cache对象
public static Cache getCacheByName(String cacheName){
Cache cache=null;
if(cacheName==null||cacheName.trim().equals("")){
System.out.println("cacheName is null");
}else{
if(getCacheManager().getCache(cacheName.trim())!=null){
cache=getCacheManager().getCache(cacheName.trim());
}
}
return cache;
}
//往缓存中添加元素
public static void putElementToCache(String cacheName,String elementKey,Object elementValue){
Cache cache=null;
if(cacheName==null||cacheName.trim().equals("")){
System.out.println("添加缓存元素失败,cacheName is null");
}else if(elementKey==null||elementValue==null){
System.out.println("添加缓存元素失败,elementKey or elementValue is null");
}else{
if(getCacheByName(cacheName.trim())!=null){//缓存存在
cache=getCacheByName(cacheName.trim());
}else{//缓存不存在
addCacheByName(cacheName.trim());
cache=getCacheByName(cacheName.trim());
}
//对cache对象添加Element
Element element=null;
if(cache.get(elementKey.trim())!=null){
cache.remove(elementKey.trim());
}
element=new Element(elementKey.trim(),elementValue);
cache.put(element);
System.out.println("添加缓存元素:"+elementKey+"成功!");
}
}
//从缓存中获取指定key的值
public static Object getElementValueFromCache(String cacheName,String elementKey){
Object result=null;
Cache cache=null;
if(cacheName==null||cacheName.trim().equals("")){
System.out.println("获取缓存元素失败,cacheName is null");
}else if(elementKey==null){
System.out.println("获取缓存元素失败,elementKey is null");
}else{
if(getCacheByName(cacheName.trim())!=null){//缓存存在
cache=getCacheByName(cacheName.trim());
Element element=null;
if(cache.get(elementKey.trim())!=null){
element=cache.get(elementKey.trim());
if(element.getObjectValue()==null){
System.out.println("缓存中"+elementKey+" 的值为空.");
}else{
result=element.getObjectValue();
}
}else{
System.out.println("缓存中"+elementKey+" 的Element值为空.");
}
}else{//缓存不存在
System.out.println("获取缓存元素失败,缓存"+cacheName+" 为空.");
}
}
return result;
}
/**
* 把所有cache中的内容删除,但是cache对象还是保留.
* Clears the contents of all caches in the CacheManager,
* but without removing any caches.
*/
public static void clearAllFromCacheManager(){
if(getCacheManager()!=null){
getCacheManager().clearAll();
System.out.println("CacheManager was clearAll...");
}
}
/**
* 把所有cache对象都删除。慎用!
* Removes all caches using removeCache(String) for each cache.
*/
public static void removalAllFromCacheManager(){
if(getCacheManager()!=null){
getCacheManager().removalAll();
System.out.println("CacheManager was removalAll...");
}
}
//不用缓存时,要关闭,不然会占用cpu和内存资源。
public static void shutdownCacheManager(){
if(getCacheManager()!=null){
getCacheManager().shutdown();
System.out.println("CacheManager was shutdown...");
}
}
//打印方法1,为了测试用
public static void printCache(Cache cache){
System.out.println("缓存状态: "+cache.getStatus().toString());
if(cache==null){
System.out.println("cache is null,no print info.");
}else if(cache.getStatus().toString().equals(Status.STATUS_UNINITIALISED)){
System.out.println("缓存状态: 未初始化"+cache.getStatus().toString());
}else if(cache.getStatus().toString().equals(Status.STATUS_SHUTDOWN)){
System.out.println("缓存状态: 已关闭"+cache.getStatus().toString());
}else if(cache.getStatus().toString().equals(Status.STATUS_ALIVE)){
if(cache.getKeys().size()==0){
System.out.println(cache.getName()+" exits,but no value.");
}else{
for(int i=0;i<cache.getKeys().size();i++){
Object thekey=cache.getKeys().get(i);
Object thevalue=cache.get(thekey);
System.out.println(cache.getName()+"--"+i+",key:"+thekey.toString()+",value:"+thevalue.toString());
}
}
}
}
//打印方法2,为了测试用
public static void printCacheByName(String cacheName){
if(cacheName==null||cacheName.trim().equals("")){
System.out.println("cacheName is null,no print info.");
}else{
if(getCacheManager().getCache(cacheName.trim())!=null){
Cache cache=getCacheManager().getCache(cacheName.trim());
printCache(cache);
}else{
System.out.println(cacheName+" --null");
}
}
}
public static void main(String[] sdfsf){
Cache cache1=EhcacheManagerTest.getCacheByName(EhcacheManagerTest.CACHE_MYCACHE1);
printCache(cache1);
EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "111", "111haah");
EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "222", "222haah");
EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "333", "333haah");
printCache(cache1);
EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "111", "111的新值。");
System.out.println(EhcacheManagerTest.getElementValueFromCache(EhcacheManagerTest.CACHE_MYCACHE1, "111"));
printCache(cache1);
clearAllFromCacheManager();
printCache(cache1);
removalAllFromCacheManager();
printCache(cache1);
shutdownCacheManager();
}
/*打印
缓存状态: STATUS_ALIVE
添加缓存元素:111成功!
添加缓存元素:222成功!
添加缓存元素:333成功!
缓存状态: STATUS_ALIVE
添加缓存元素:111成功!
111的新值。
缓存状态: STATUS_ALIVE
CacheManager was clearAll...
缓存状态: STATUS_ALIVE
CacheManager was removalAll...
缓存状态: STATUS_SHUTDOWN
CacheManager was shutdown...
*/
}
通过上面的使用,我们初步了解Ehcache的api。在web环境下,我们可以注入EhcacheManager对象,
把需要的数据,放入缓存。在其他地方取数据的时候,就从过EhcacheManager来获取,而不是直接
从数据库查询。这样就提高了效率。
值得注意的是,缓存的使用一般在一些数据比较固定的地方。如果某个查询需要保证数据的实时性,使用缓存
就是错误的做法。