14.1 Redis入门
14.2 Redis数据模型
14.3 Jedis基础编程的实践案例
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency>
14.3.1 Jedis操作String字符串
@Testpublic void operateString() {Jedis jedis = new Jedis("localhost", 6379);//如果返回 pang 代表链接成功Logger.info("jedis.ping():" + jedis.ping());//设置key0的值 123456jedis.set("key0", "123456");//返回数据类型 stringLogger.info("jedis.type(key0): " + jedis.type("key0"));//get keyLogger.info("jedis.get(key0): " + jedis.get("key0"));// key是否存在Logger.info("jedis.exists(key0):" + jedis.exists("key0"));//返回key的长度Logger.info("jedis.strlen(key0): " + jedis.strlen("key0"));//返回截取字符串, 范围 0,-1 表示截取全部Logger.info("jedis.getrange(key0): " + jedis.getrange("key0", 0, -1));//返回截取字符串, 范围 1,4 表示从表示区间[1,4]Logger.info("jedis.getrange(key0): " + jedis.getrange("key0", 1, 4));//追加Logger.info("jedis.append(key0): " + jedis.append("key0", "appendStr"));Logger.info("jedis.get(key0): " + jedis.get("key0"));//重命名jedis.rename("key0", "key0_new");//判断key 是否存在Logger.info("jedis.exists(key0): " + jedis.exists("key0"));//批量插入jedis.mset("key1", "val1", "key2", "val2", "key3", "100");//批量取出Logger.info("jedis.mget(key1,key2,key3): " + jedis.mget("key1", "key2", "key3"));//删除Logger.info("jedis.del(key1): " + jedis.del("key1"));Logger.info("jedis.exists(key1): " + jedis.exists("key1"));//取出旧值 并set新值Logger.info("jedis.getSet(key2): " + jedis.getSet("key2", "value3"));//自增1 要求数值类型Logger.info("jedis.incr(key3): " + jedis.incr("key3"));//自增15 要求数值类型Logger.info("jedis.incrBy(key3): " + jedis.incrBy("key3", 15));//自减1 要求数值类型Logger.info("jedis.decr(key3): " + jedis.decr("key3"));//自减5 要求数值类型Logger.info("jedis.decrBy(key3): " + jedis.decrBy("key3", 15));//增加浮点类型Logger.info("jedis.incrByFloat(key3): " + jedis.incrByFloat("key3", 1.1));//返回0 只有在key不存在的时候才设置Logger.info("jedis.setnx(key3): " + jedis.setnx("key3", "existVal"));Logger.info("jedis.get(key3): " + jedis.get("key3"));// 3.1//只有key都不存在的时候才设置,这里返回 nullLogger.info("jedis.msetnx(key2,key3): " + jedis.msetnx("key2", "exists1", "key3", "exists2"));Logger.info("jedis.mget(key2,key3): " + jedis.mget("key2", "key3"));//设置key 2 秒后失效jedis.setex("key4", 2, "2 seconds is no Val");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 2 seconds is no ValLogger.info("jedis.get(key4): " + jedis.get("key4"));jedis.set("key6", "123456789");//下标从0开始,从第三位开始,将新值覆盖旧值jedis.setrange("key6", 3, "abcdefg");//返回:123abcdefgLogger.info("jedis.get(key6): " + jedis.get("key6"));//返回所有匹配的keyLogger.info("jedis.get(key*): " + jedis.keys("key*"));jedis.close();}
14.3.2 Jedis操作List列表
@Testpublic void operateList() {Jedis jedis = new Jedis("localhost");Logger.info("jedis.ping(): " +jedis.ping());jedis.del("list1");//从list尾部添加3个元素jedis.rpush("list1", "zhangsan", "lisi", "wangwu");//取得类型, listLogger.info("jedis.type(): " +jedis.type("list1"));//遍历区间[0,-1],取得全部的元素Logger.info("jedis.lrange(0,-1): " +jedis.lrange("list1", 0, -1));//遍历区间[1,2],取得区间的元素Logger.info("jedis.lrange(1,2): " +jedis.lrange("list1", 1, 2));//获取list长度Logger.info("jedis.llen(list1): " +jedis.llen("list1"));//获取下标为 1 的元素Logger.info("jedis.lindex(list1,1): " +jedis.lindex("list1", 1));//左侧弹出元素Logger.info("jedis.lpop(): " +jedis.lpop("list1"));//右侧弹出元素Logger.info("jedis.rpop(): " +jedis.rpop("list1"));//设置下标为0的元素valjedis.lset("list1", 0, "lisi2");//最后,遍历区间[0,-1],取得全部的元素Logger.info("jedis.lrange(0,-1): " +jedis.lrange("list1", 0, -1));jedis.close();}
14.3.3 Jedis操作Hash哈希表
@Testpublic void operateHash() {Jedis jedis = new Jedis("localhost");jedis.del("config");//设置hash的 field-value 对jedis.hset("config", "ip", "127.0.0.1");//取得hash的 field的关联的value值Logger.info("jedis.hget(): " + jedis.hget("config", "ip"));//取得类型:hashLogger.info("jedis.type(): " + jedis.type("config"));//批量添加 field-value 对,参数为java mapMap<String, String> configFields = new HashMap<String, String>();configFields.put("port", "8080");configFields.put("maxalive", "3600");configFields.put("weight", "1.0");//执行批量添加jedis.hmset("config", configFields);//批量获取:取得全部 field-value 对,返回 java mapLogger.info("jedis.hgetAll(): " + jedis.hgetAll("config"));//批量获取:取得部分 field对应的value,返回 java mapLogger.info("jedis.hmget(): " + jedis.hmget("config", "ip", "port"));//浮点数增加: 类似于String的 incrByFloatjedis.hincrByFloat("config", "weight", 1.2);Logger.info("jedis.hget(weight): " + jedis.hget("config", "weight"));//获取所有的keyLogger.info("jedis.hkeys(config): " + jedis.hkeys("config"));//获取所有的valLogger.info("jedis.hvals(config): " + jedis.hvals("config"));//获取长度Logger.info("jedis.hlen(): " + jedis.hlen("config"));//判断field是否存在Logger.info("jedis.hexists(weight): " + jedis.hexists("config", "weight"));//删除一个fieldjedis.hdel("config", "weight");Logger.info("jedis.hexists(weight): " + jedis.hexists("config", "weight"));jedis.close();}
14.3.4 Jedis操作Set集合
@Testpublic void operateSet() {Jedis jedis = new Jedis("localhost");jedis.del("set1");Logger.info("jedis.ping(): " + jedis.ping());Logger.info("jedis.type(): " + jedis.type("set1"));//sadd函数: 向集合添加元素jedis.sadd("set1", "user01", "user02", "user03");//smembers函数: 遍历所有元素Logger.info("jedis.smembers(): " + jedis.smembers("set1"));//scard函数: 获取集合元素个数Logger.info("jedis.scard(): " + jedis.scard("set1"));//sismember 判断是否是集合元素Logger.info("jedis.sismember(user04): " + jedis.sismember("set1", "user04"));//srem函数:移除元素Logger.info("jedis.srem(): " + jedis.srem("set1", "user02", "user01"));//smembers函数: 遍历所有元素Logger.info("jedis.smembers(): " + jedis.smembers("set1"));jedis.close();}
14.3.5 Jedis操作Zset有序集合
@Testpublic void operateZset() {Jedis jedis = new Jedis("localhost");Logger.info("jedis.get(): " + jedis.ping());jedis.del("salary");Map<String, Double> members = new HashMap<String, Double>();members.put("u01", 1000.0);members.put("u02", 2000.0);members.put("u03", 3000.0);members.put("u04", 13000.0);members.put("u05", 23000.0);//批量添加元素jedis.zadd("salary", members);//类型,zsetLogger.info("jedis.type(): " + jedis.type("salary"));//获取集合元素个数Logger.info("jedis.zcard(): " + jedis.zcard("salary"));//按照下标[起,止]遍历元素Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1));//按照下标[起,止]倒序遍历元素Logger.info("jedis.zrevrange(): " + jedis.zrevrange("salary", 0, -1));//按照分数(薪资)[起,止]遍历元素Logger.info("jedis.zrangeByScore(): " + jedis.zrangeByScore("salary", 1000, 10000));//按照薪资[起,止]遍历元素,带分数返回Set<Tuple> res0 = jedis.zrangeByScoreWithScores("salary", 1000, 10000);for (Tuple temp : res0) {Logger.info("Tuple.get(): " + temp.getElement() + " -> " + temp.getScore());}//按照分数[起,止]倒序遍历元素Logger.info("jedis.zrevrangeByScore(): " + jedis.zrevrangeByScore("salary", 1000, 4000));//获取元素[起,止]分数区间的元素数量Logger.info("jedis.zcount(): " + jedis.zcount("salary", 1000, 4000));//获取元素score值:薪资Logger.info("jedis.zscore(): " + jedis.zscore("salary", "u01"));//获取元素下标Logger.info("jedis.zrank(u01): " + jedis.zrank("salary", "u01"));//倒序获取元素下标Logger.info("jedis.zrevrank(u01): " + jedis.zrevrank("salary", "u01"));//删除元素Logger.info("jedis.zrem(): " + jedis.zrem("salary", "u01", "u02"));//删除元素,通过下标范围Logger.info("jedis.zremrangeByRank(): " + jedis.zremrangeByRank("salary", 0, 1));//删除元素,通过分数范围Logger.info("jedis.zremrangeByScore(): " + jedis.zremrangeByScore("salary", 20000, 30000));//按照下标[起,止]遍历元素Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1));Map<String, Double> members2 = new HashMap<String, Double>();members2.put("u11", 1136.0);members2.put("u12", 2212.0);members2.put("u13", 3324.0);//批量添加元素jedis.zadd("salary", members2);//增加指定分数Logger.info("jedis.zincrby(10000): " + jedis.zincrby("salary", 10000, "u13"));//按照下标[起,止]遍历元素Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1));jedis.close();}
14.4 JedisPool连接池的实践案例
14.4.1 JedisPool的配置
(1) maxTotal:资源池中最大的连接数,默认值为8。
(2) maxIdle:资源池允许最大空闲的连接数,默认值为8。
(3) minIdle:资源池确保最少空闲的连接数,默认值为0。
(4) blockWhenExhausted:当资源池用尽后,调用者是否要等待,默认值为true。
(5) maxWaitMillis:当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)
(6) testOnBorrow:向资源池借用连接时,是否做有效性检测(ping命令),如果是无效连接,会被移除,默认值为false,表示不做检测。
(7) testOnReturn:向资源池归还连接时,是否做有效性检测(ping命令),如果是无效连接,会被移除,默认值为false,表示不做检测。
(8) testWhileIdle:如果为true,表示用一个专门的线程对空闲的连接进行有效性的检测扫描,如果连接的有效性检测失败,即表示监测到无效连接,会从资源池中移除。
(9) timeBetweenEvictionRunsMillis:表示两次空闲连接扫描的间隔时间, 默认为30000毫秒,也就是30秒钟。
(10) minEvictableIdleTimeMillis:表示一个Jedis连接至少停留在空闲状态的最短时间,然后才能被空闲连接扫描线程进行有效性检测,默认值为60000毫秒,即60秒。
(11) numTestsPerEvictionRun:表示空闲检测线程每次最多扫描的Jedis连接数,默认值为-1,表示扫描全部的空闲连接。
(12) jmxEnabled:是否开启JMX监控,默认值为true,建议开启。
14.4.2 JedisPool创建和预热
private static JedisPool buildPool() {if (pool == null) {long start = System.currentTimeMillis();JedisPoolConfig config = new JedisPoolConfig();config.setMaxIdle(MAX_IDLE);config.setMaxTotal(MAX_TOTAL);config.setMaxWaitMillis(1000 * 10);// 在 borrow 一个 jedis 实例时,是否提前进行有效检测操作;// 如果为 true,则得到的 jedis 实例均是可用的;config.setTestOnBorrow(true);pool = new JedisPool(config, "101.132.251.198", 6379, 10000);long end = System.currentTimeMillis();Logger.info("buildPool 毫秒数:", end - start);}return pool;}
预热:
public static void hotPool() {long start = System.currentTimeMillis();List<Jedis> minIdleJedisList = new ArrayList<>(MAX_IDLE);Jedis jedis = null;for (int i = 0; i < MAX_IDLE; i++) {try {jedis = pool.getResource();minIdleJedisList.add(jedis);jedis.ping();}catch (Exception e) {Logger.error(e.getMessage());}}for (int i = 0; i < MAX_IDLE; i++) {try {jedis = minIdleJedisList.get(i);jedis.close();}catch (Exception e) {Logger.error(e.getMessage());}}long end = System.currentTimeMillis();Logger.info("hotPool 毫秒数:", end - start);}
创建好连接池实例,并且进行预热
static {buildPool();hotPool();}public synchronized static Jedis getJedis() {return pool.getResource();}
14.4.3 JedisPool的使用
@Testpublic void testDel() {Jedis redis = null;try {redis = JedisPoolBuilder.getJedis();long start = System.currentTimeMillis();redis.del(ZSET_KEY);long end = System.currentTimeMillis();log.info("删除 zset1 毫秒数:", end - start);}finally {//使用后一定关闭,还给连接池if (redis != null) {redis.close();}}}
使用try-with-resources语句,在其隐藏的finally部分自动调用close方法。
@Testpublic void testSet() {testDel();try (Jedis redis = JedisPoolBuilder.getJedis()){int loop = 0;long start = System.currentTimeMillis();while (loop < NUM) {redis.zadd(ZSET_KEY, loop, "field-" + loop);loop++;}long end = System.currentTimeMillis();Logger.info("设置 Zset :", loop, "次, 毫秒数:", end - start);}}
14.5 使用spring-data-redis完成CRUD的实践案例
<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>${springboot}</version></dependency>
14.5.1 CRUD中应用缓存的场景
14.5.3 使用RedisTemplate模板API
(1) ValueOperations字符串类型操作API集合。
(2) ListOperations列表类型操作API集合。
(3) SetOperations集合类型操作API集合。
(4) ZSetOperations有序集合类型API集合。
(5) HashOperations哈希类型操作API集合。
在实际开发中,为了尽可能地减少对特定的第三方库的依赖,减少第三方库的“入侵”性,或者为了在不同的第三方库之间进行方便的切换,一般来说,要对第三方库进行 封装
public class CacheOperationService {private RedisTemplate redisTemplate;public void setRedisTemplate(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}// --------------RedisTemplate 基础操作 --------------------/*** 取得指定格式的所有的key** @param patens 匹配的表达式* @return key 的集合*/public Set getKeys(Object patens) {try {return redisTemplate.keys(patens);} catch (Exception e) {e.printStackTrace();return null;}}/*** 指定缓存失效时间** @param key 键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存** @param key 可以传一个值 或多个* @return 删除的个数*/@SuppressWarnings("unchecked")public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}// --------------RedisTemplate 操作 String --------------------/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key 键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** @param key 键* @param value 值* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增** @param key 键* @param delta 自增因子,要增加几(大于0)* @return 自增的结果*/public long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("自增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 自减** @param key 键* @param delta 自减, 要减少几(小于0)* @return*/public long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("自减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}// --------------RedisTemplate 操作 Map --------------------/*** HashGet** @param key 键 不能为null* @param field 项 不能为null* @return 值*/public Object hget(String key, String field) {return redisTemplate.opsForHash().get(key, field);}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String, Object> map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间** @param key 键* @param map 对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param field 项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String field, Object value) {try {redisTemplate.opsForHash().put(key, field, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param field 项* @param value 值* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String field, Object value, long time) {try {redisTemplate.opsForHash().put(key, field, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** @param key 键 不能为null* @param field 项 可以使多个 不能为null*/public void hdel(String key, Object... field) {redisTemplate.opsForHash().delete(key, field);}/*** 判断hash表中是否有该项的值** @param key 键 不能为null* @param field 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String field) {return redisTemplate.opsForHash().hasKey(key, field);}/*** hash自增 如果不存在,就会创建一个 并把新增后的值返回** @param key 键* @param field 项* @param by 要增加几(大于0)* @return*/public double hincr(String key, String field, double by) {return redisTemplate.opsForHash().increment(key, field, by);}/*** hash自减** @param key 键* @param field 项* @param by 要减少记(小于0)* @return*/public double hdecr(String key, String field, double by) {return redisTemplate.opsForHash().increment(key, field, -by);}// --------------RedisTemplate 操作 Set --------------------/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** @param key 键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** @param key 键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** @param key 键* @param time 时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0)expire(key, time);return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** @param key 键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}// --------------RedisTemplate 操作list --------------------/*** 获取list缓存的内容** @param key 键* @param start 开始* @param end 结束 0 到 -1代表所有值* @return*/public List<Object> lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** @param key 键* @return*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** @param key 键* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存** @param key 键* @param value 值* @return*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time > 0)expire(key, time);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @return*/public boolean lSet(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time > 0)expire(key, time);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** @param key 键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value** @param key 键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}}
redisTemplate提供了5个方法,取得不同类型的命令集合,具体为:
(1) redisTemplate.opsForValue()取得String类型命令API集合。
(2) redisTemplate.opsForList()取得List类型命令API集合。
(3) redisTemplate.opsForSet()取得Set类型命令API集合。
(4) redisTemplate.opsForHash()取得Hash类型命令API集合。
(5) redisTemplate.opsForZSet()取得Zset类型命令API集合
15.5.4 使用Redis Template模板API完成CRUD的实践案例
public class UserServiceImpleWithTemplate implements UserService{public static final String USER_UID_PREFIX = "user:uid:";protected CacheOperationService cacheOperationService;private static final long CASHE_LONG = 60 * 4;@Overridepublic User saveUser(User user) {String key = USER_UID_PREFIX + user.getUid();Logger.info("user:", user);cacheOperationService.set(key, user, CASHE_LONG);return user;}@Overridepublic User getUser(long id) {String key = USER_UID_PREFIX + id;User value = (User) cacheOperationService.get(key);if (null == value) {return null;}return value;}@Overridepublic void deleteUser(long id) {String key = USER_UID_PREFIX + id;cacheOperationService.del(key);Logger.info("delete User: ", id);}}
14.5.5 使用RedisCallback回调完成CRUD的实践案例
如果不需要RedisTemplate配置的序列化、反序列化的工具类,或者由于其他的原因,需要直接使用RedisConnection去操作Redis,怎么办呢?可以使用RedisCallback的doInRedis回调方法,在doInRedis回调方法中直接使用实参RedisConnection连接类实例来完成Redis的操作。
14.6 Spring的Redis缓存注解
在Spring中,通过合理的添加缓存注解,也能实现和前面示例程序中一样的缓存功能。
为了方便地提供缓存能力, Spring提供了一组缓存注解。但是,这组注解不仅仅是针对Redis, 这组注解本质上并不是一种具体的缓存实现方案(例如Redis、 EHCache等),而是对缓存使用的统一抽象。通过这组缓存注解,然后加上与具体缓存相匹配的Spring配置,不用编码就可以快速达到缓存的效果。
14.6.1 使用Spring缓存注解完成CRUD的实践案例
@CachePut作用是设置缓存。先执行方法,并将执行结果缓存起来。@CacheEvict的作用是删除缓存。在执行方法后,删除缓存。@Cacheable的作用更多是查询缓存。首先检查注解中的Key键是否在缓存中,如果是,则返回Key的缓存值,不再执行方法;否则,执行方法并将方法结果缓存起来。从后半部分来看,@Cacheable也具备@CachePut的能力。
public class UserServiceImplWithAnno implements UserService{public static final String USER_UID_PREFIX = "'userCache:'+";@CachePut(key = USER_UID_PREFIX + "T(String).valueOf(#user.uid)")@Overridepublic User saveUser(User user) {Logger.info("user: save to redis");return user;}@Cacheable(key = USER_UID_PREFIX + "T(String).valueOf(#id)")@Overridepublic User getUser(long id) {//如果缓存没有,则从数据库中加载Logger.info("user: is null");return null;}@CacheEvict(key = USER_UID_PREFIX + "T(String).valueOf(#id)")@Overridepublic void deleteUser(long id) {Logger.info("delete User:", id);}}
14.6.3 详解
@CachePut和@Cacheable注解@CachePut负责增加缓存。@Cacheable负责查询缓存,如果没有查到, 才去执行被注解的方法,并将方法
的结果增加到缓存。14.6.4 详解
@CacheEvict注解注解@CacheEvict主要用来清除缓存,可以指定的属性有value、 key、 condition、allEntries和beforeInvocation。其中value、 key和condition的语义与@Cacheable对应的属性类似。 value表示清除哪些Cache(对应Cache的空间名称); key表示清除哪个Key键;condition表示清除的条件。
allEntries属性:表示是否全部清空
beforeInvocation属性:表示是否在方法执行前操作缓存
14.6.5 详解
@Caching组合注解@Caching注解,是一个缓存处理的组合注解。通过@Caching,可以一次指定多个Spring Cache注解的组合。 @Caching注解拥有三个属性:cacheable、 put和evict。
cacheable属性:用于指定一个或者多个@Cacheable注解的组合,可以指定一个,也可以指定多个。
- put属性:用于指定一个或者多个@CachePut注解的组合,可以指定一个,也可以指定多个,用于设置一个或多个key的缓存。
- evict属性:用于指定一个或者多个@CacheEvict注解的组合,可以指定一个,也可以指定多个,用于删除一个或多个key的缓存。
14.7 详解SpringEL(SpEL)
Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”。 SpEL提供一种强大、简洁的Spring Bean的动态操作表达式。
