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