Redis + EJB 缓存实现(四)—KEY值生成策略
2015-05-19 23:50
288 查看
上一篇博客针对Key值生成策略和Redis数据源读取的问题,想了一些解决的思路,停滞了一段时间后,最近实现好了。下面就根据实现,再结合解决思路说明一下。本篇博客先说KEY值生成策略的问题。
图中①和②分别表明了两个加数据缓存的位置,另外如图,远程调用是发生在EJB容器之间的。另外,查询方法的结果加入缓存后还需要清除在增、删、改这些操作的时候清除缓存,以保证不出现脏数据。如果是在模块1的①的位置加入数据缓存,如果缓存存在时就不会再往后调用了。而问题是如果这个方法的数据涉及到了其模块2的数据,模块2的数据变更是无法通知到模块1的IOC容器的。那么脏数据就无法避免了。
由上可知,数据缓存需要加在②位置,并且是没有发生远程调用的方法上。因为同一个类上的对数据操作是可知的。这样,加缓存和清缓存的操作就可知了。
这个前提决定了缓存的操作可以精确到类,也就决定了KEY值的生成策略(请看上篇博客:Redis+EJB实现缓存(三)),加缓存的KEY值可以以类名+方法名+参数,删除缓存则以类为单位删除。
这样,键值的生成策略就算是基本完成了。但是后面思考了一下,当参数类型是Object时还有问题。
问题描述
Object的toString方法,是getClass().getName()+ '@' + Integer.toHexString(hashCode())这个hashCode只是标明了类的在hashTable中的位置。换句话说,两个一样的类由于两次生成的类,可能不存在同一个位置下。那么Object参数作为键值时就无法精确定位同一个键值,那么这样在清除缓存时就无法清除了。
小结,新的问题,明天就实现,应该是可行。下篇博客我们再说,Redis数据源的解决实现。这篇博客就到这里了。上面的代码主要看注释的那部分。
前提
我们的系统是Spring mvc + EJB实现的。因为分布式调用的问题,为了避免脏数据我们的数据缓存需要加在Service层。这个之前没有交代过,通过下面这个图,解释一下:图中①和②分别表明了两个加数据缓存的位置,另外如图,远程调用是发生在EJB容器之间的。另外,查询方法的结果加入缓存后还需要清除在增、删、改这些操作的时候清除缓存,以保证不出现脏数据。如果是在模块1的①的位置加入数据缓存,如果缓存存在时就不会再往后调用了。而问题是如果这个方法的数据涉及到了其模块2的数据,模块2的数据变更是无法通知到模块1的IOC容器的。那么脏数据就无法避免了。
由上可知,数据缓存需要加在②位置,并且是没有发生远程调用的方法上。因为同一个类上的对数据操作是可知的。这样,加缓存和清缓存的操作就可知了。
这个前提决定了缓存的操作可以精确到类,也就决定了KEY值的生成策略(请看上篇博客:Redis+EJB实现缓存(三)),加缓存的KEY值可以以类名+方法名+参数,删除缓存则以类为单位删除。
加缓存的拦截器方法实现
public Object cache(InvocationContext ctx) throws Exception{ System.out.println("进入拦截器"); if(cachepro.getFlag() == false){ InputStream in = (ctx.getTarget().getClass().getResourceAsStream("/config/cache.xml")); byte[] byt = new byte[in.available()]; in.read(byt); cache.init(byt); } //取得类名 String className = ctx.getTarget().getClass().getSimpleName(); //取得方法名 String methodName = ctx.getMethod().getName(); //取得参数 Object[] args = ctx.getParameters(); String arg = ""; for(int i=0;i<args.length;i++) { arg = arg + args[i].toString(); } //主键值,由系统名称+类名,删除时用 String mainKey = cachepro.getPrefixion() + className; //次级键值由主键值再加上方法名和参数 String key = mainKey + methodName + arg; //次级键值存在则返回结果 if(cache.ishave(key)){ return cache.get(key); }else{ //次级键值不存在则将次级键值存入主键值下 Object result = ctx.proceed(); cache.set(mainKey, key); cache.set(key,result); return null; } }
删除缓存拦截器方法实现
public Object cache(InvocationContext ctx) throws Exception{ if(cachepro.getFlag() == false){ InputStream in = (ctx.getTarget().getClass().getResourceAsStream("/config/cache.xml")); byte[] byt = new byte[in.available()]; in.read(byt); cache.init(byt); } //取得类名 String className = ctx.getTarget().getClass().getSimpleName(); //系统名+类名生成主键值,对应添加时的 String mainKey = cachepro.getPrefixion() + className; //删除时,取得主键值下的次级键值,删除缓存 cache.del(mainKey); return null; }
这样,键值的生成策略就算是基本完成了。但是后面思考了一下,当参数类型是Object时还有问题。
问题描述
Object的toString方法,是getClass().getName()+ '@' + Integer.toHexString(hashCode())这个hashCode只是标明了类的在hashTable中的位置。换句话说,两个一样的类由于两次生成的类,可能不存在同一个位置下。那么Object参数作为键值时就无法精确定位同一个键值,那么这样在清除缓存时就无法清除了。
解决思路
作为参数的Object都是实体,因此我们需要重写toString方法,使得类中的字段一样时返回的String是一样的。这样就解决了,两个实体属性一样的类作为参数时,生成的键值是一样的。问题就解决了。小结,新的问题,明天就实现,应该是可行。下篇博客我们再说,Redis数据源的解决实现。这篇博客就到这里了。上面的代码主要看注释的那部分。
相关文章推荐
- Redis + EJB缓存实现(五)—配置文件
- Redis+EJB实现缓存(一)
- Redis+EJB缓存实现(三)
- Redis+EJB实现缓存(一)
- Redis + EJB 实现缓存(二)
- mybatis缓存 redis实现
- spring+springmvc+mybatis+redis 实现两重数据缓存
- Java互联网架构-Redis分布式缓存架构实现与原理解析
- Redis 高可用: twemproxy实现缓存服务器分片集群
- SpringMVC通过Redis实现缓存主页
- spring + redis 实现数据的缓存
- EJB3下使用Ehcache实现二级缓存
- SpingAOP+redis实现缓存
- SpringBoot中使用Redis实现缓存
- Spring Boot集成Redis实现缓存
- 使用Spring Data +Redis实现缓存
- spring + redis 实现数据的缓存
- nginx+redis 实现 jsp页面缓存,提升系统吞吐率
- Spring Boot 整合 Redis 实现缓存操作
- Spring-Boot 集成Redis实现查询缓存提高查询效率减轻数据库访问压力(涉及key的添加和删除)