在看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来获取,而不是直接
从数据库查询。这样就提高了效率。
值得注意的是,缓存的使用一般在一些数据比较固定的地方。如果某个查询需要保证数据的实时性,使用缓存
就是错误的做法。