简单说下我一开始在grails项目中使用redis的过程。
一开始,我根据 这个网站的https://github.com/grails-plugins/grails-redis 提供的方式,使用的是注解的方式进行数据的缓存。我用的可嗨了。结果自测的时候才发现,这里面的注解 (
memoizeHash 只能放String类型的数据, )
其他的具体的,大家可以进到RedisService里面自己看。我现在主要说说我自己实现的方式。
我拿一个Map 里面放domain和其他数据类型的值来说把。
这是我写的一个方法。可以支持Map里面有domain。Map中有map。
@Transactional class MemoizeService { //用户序列化和反序列化的类 RedisSerializer valueSerializer; // redisService类 def redisService /** * * 1:存储 List<Map>的数据 * * @param key * @param closure * @param expire * @return * * 备注:List中的map中不支持domain */ def memoizeListObject(String key, Closure closure, int expire = 0) { // 1:查询缓存, byte[] keyByte = valueSerializer.serialize(key); def list = redisService.withRedis { Jedis redis -> if (redis) { log.debug("自定义的memoizeListObject方法---根据key获取值。"); List<byte[]> list = redis.lrange(keyByte, 0, -1);// List<Object> value = handleList(list, false); //打印日志 if (value) { log.debug("自定义的memoizeListObject方法---根据key=${key},获取值=${value}"); } return value } } //2:如果缓存中无数据,进行数据库的查询。 if (!list) { //存缓存 if (log.debugEnabled) log.debug "cache miss: $key" list = closure();//调用目标方法,返回值里面,我们需要判断每个值的类型。 redisService.withRedis { Jedis redis -> if (redis) { if (list && list.size() > 0) { List<byte[]> listTemp = handleList((List) list);//序列化 redis.rpush(keyByte, (byte[][]) listTemp.toArray());//存入缓存 if (expire) { redis.expire(keyByte, expire);//设置缓存时间 } } } } } else { if (log.debugEnabled) log.debug "cache hit : $key = $list" } return list //返回值 } /** * 2:缓存Map中存储复杂对象的数据 * a:支持map中有domain对象 * b:支持map中有List<Map>数据 * c:未支持map中有map的数据。 * * @param key 缓存数据的key值 * @param closure 执行对象的目标方法 * @param expire 缓存时间 * @return 缓存的map对象/数据库中查询的对象 */ def memoizeMap(String key, Closure closure, int expire = 0) { //1:序列化key值 byte[] keyByte = valueSerializer.serialize(key); //2:查询缓存 def hash = redisService.withRedis { Jedis redis -> if (redis) { log.debug("自定义的memoireMap方法---根据key获取值。"); Map map = [:];//返回值的map Map<byte[], byte[]> hashMap = redis.hgetAll(keyByte);//这个是获取map的所有值的方法 if (hashMap) { map = deserializeMap(hashMap); log.debug("自定义的memoireMap方法---根据key=${key},获取值=${map}"); } return map } } //3:如果缓存中无数据,就调用目标方法和存储缓存。 if (!hash) { if (log.debugEnabled) log.debug "cache miss: $key" hash = closure();//调用目标方法,返回值里面,我们需要判断每个值的类型。 if (hash) { redisService.withRedis { Jedis redis -> if (redis) { Map<byte[], byte[]> tempHash = serializeMap(hash) log.debug("进行数据缓存:键=${key},值=${hash}") redis.hmset(keyByte, tempHash) if (expire) { redis.expire(keyByte, expire) } } } } } else { if (log.debugEnabled) log.debug "cache hit : $key = $hash" } return hash //返回值en } /** * 处理 List 数据 * * @param isSerialize 是否序列化 * @param list 需要处理的数据 * @return 返回处理过后的数据 */ public List<Object> handleList(List<Object> list, boolean isSerialize = true) { //1:作基本判断 if (!list) { return null;//待处理的数据不能为空 } //2:处理数据 List<Object> targetList = []; if (isSerialize) { for (Object o : list) { targetList.add(valueSerializer.serialize(o))//序列化 } } else { for (byte[] bt : list) { targetList.add(valueSerializer.deserialize(bt)) //反序列化 } } //3;返回数据 return targetList; } /** * * hashMap:待处理的值 * map :返回的值 * * 反序列化 */ private Map deserializeMap(Map hashMap) { Map map = [:]; for (byte[] hashKey : hashMap.keySet()) { def _key = valueSerializer.deserialize(hashKey); def _value = valueSerializer.deserialize(hashMap[hashKey]); if (_value && _value instanceof String && _value.toString().startsWith("DOMAIN#")) { // domain ,可支持 def parts = _value.toString().split("#"); map.put(_key, Class.forName(parts[1]).load(parts[2]));//存入键值 } else if (_value && Map.class.isAssignableFrom(_value.getClass())) {// 有问题,还不能支持 map.put(_key, _value);//Map } else if (_key && _key.toString().contains("List")) { //支持Map中存入List。 List list = []; for (Object obj : (List) _value) { def _obj = valueSerializer.deserialize(obj); if (_obj && _obj instanceof String && _obj.toString().startsWith("DOMAIN#")) { // domain def parts = _obj.toString().split("#"); list.add(Class.forName(parts[1]).load(parts[2]));//存入键值 } else { list.add(_obj); } } def parts = _key.toString().split("#");// map.put(parts[0], list) } else { map.put(_key, _value);//存入键值 } } return map; } /** * * 序列化数据 * * @param hash 待处理的值, * @param tempHash 返回的值,Map<byte[], byte[]> */ private Map serializeMap(Map hash) { Map tempHash = [:] for (Object hashKey : hash.keySet()) { def value = hash[hashKey] //Map里面有domain if (value && value.class?.getAnnotation(Entity.class)) { // domain tempHash.put(valueSerializer.serialize(hashKey), valueSerializer.serialize("DOMAIN#${value.class.name}#${value.id}".toString())) } //Map里面有Map,二层的Map不能支持Domain else if (value && Map.class.isAssignableFrom(value.getClass())) { // tempHash.put(valueSerializer.serialize(hashKey), valueSerializer.serialize(value))// } else if (List.class.isAssignableFrom(value.getClass())) { // 支持Map中存入List List list = []; for (Object obj : (List) value) { if (obj && obj.getClass()?.getAnnotation(Entity.class)) { // domain list.add(valueSerializer.serialize("DOMAIN#${value.class.name}#${obj.id}".toString())) } else { list.add(valueSerializer.serialize(obj)); } } tempHash.put(valueSerializer.serialize(hashKey + "#List"), valueSerializer.serialize(list)) } //其他情况 else { tempHash.put(valueSerializer.serialize(hashKey), valueSerializer.serialize(hash[hashKey])); } } return tempHash } }
调用方式:以调用List<Map>为例。
@Transactional(readOnly = true) public List<Map> findMaterialAuth(Long materialId, int expire = 3600) { return memoizeService.memoizeListObject("detailCacheService_findMaterialAuth_${materialId}", { //认证信息 List<MaterialAuth> materialAuths = MaterialAuth.createCriteria().list { eq("material.id", materialId) order("id", "asc") order("seq", "asc") } List<Map> materialAuthList = new ArrayList() materialAuths.each { def materialAuthMap = [:] materialAuthMap.put("item", it.item)//认证项目 materialAuthMap.put("imgUrl", it.imgUrl)//认证图片 materialAuthMap.put("imgTitle", it.imgTitle)//图片标题 materialAuthList << materialAuthMap } return materialAuthList }, expire) }