14.1 Redis入门

14.2 Redis数据模型

14.3 Jedis基础编程的实践案例

  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>2.9.0</version>
  5. </dependency>

14.3.1 Jedis操作String字符串

  1. @Test
  2. public void operateString() {
  3. Jedis jedis = new Jedis("localhost", 6379);
  4. //如果返回 pang 代表链接成功
  5. Logger.info("jedis.ping():" + jedis.ping());
  6. //设置key0的值 123456
  7. jedis.set("key0", "123456");
  8. //返回数据类型 string
  9. Logger.info("jedis.type(key0): " + jedis.type("key0"));
  10. //get key
  11. Logger.info("jedis.get(key0): " + jedis.get("key0"));
  12. // key是否存在
  13. Logger.info("jedis.exists(key0):" + jedis.exists("key0"));
  14. //返回key的长度
  15. Logger.info("jedis.strlen(key0): " + jedis.strlen("key0"));
  16. //返回截取字符串, 范围 0,-1 表示截取全部
  17. Logger.info("jedis.getrange(key0): " + jedis.getrange("key0", 0, -1));
  18. //返回截取字符串, 范围 1,4 表示从表示区间[1,4]
  19. Logger.info("jedis.getrange(key0): " + jedis.getrange("key0", 1, 4));
  20. //追加
  21. Logger.info("jedis.append(key0): " + jedis.append("key0", "appendStr"));
  22. Logger.info("jedis.get(key0): " + jedis.get("key0"));
  23. //重命名
  24. jedis.rename("key0", "key0_new");
  25. //判断key 是否存在
  26. Logger.info("jedis.exists(key0): " + jedis.exists("key0"));
  27. //批量插入
  28. jedis.mset("key1", "val1", "key2", "val2", "key3", "100");
  29. //批量取出
  30. Logger.info("jedis.mget(key1,key2,key3): " + jedis.mget("key1", "key2", "key3"));
  31. //删除
  32. Logger.info("jedis.del(key1): " + jedis.del("key1"));
  33. Logger.info("jedis.exists(key1): " + jedis.exists("key1"));
  34. //取出旧值 并set新值
  35. Logger.info("jedis.getSet(key2): " + jedis.getSet("key2", "value3"));
  36. //自增1 要求数值类型
  37. Logger.info("jedis.incr(key3): " + jedis.incr("key3"));
  38. //自增15 要求数值类型
  39. Logger.info("jedis.incrBy(key3): " + jedis.incrBy("key3", 15));
  40. //自减1 要求数值类型
  41. Logger.info("jedis.decr(key3): " + jedis.decr("key3"));
  42. //自减5 要求数值类型
  43. Logger.info("jedis.decrBy(key3): " + jedis.decrBy("key3", 15));
  44. //增加浮点类型
  45. Logger.info("jedis.incrByFloat(key3): " + jedis.incrByFloat("key3", 1.1));
  46. //返回0 只有在key不存在的时候才设置
  47. Logger.info("jedis.setnx(key3): " + jedis.setnx("key3", "existVal"));
  48. Logger.info("jedis.get(key3): " + jedis.get("key3"));// 3.1
  49. //只有key都不存在的时候才设置,这里返回 null
  50. Logger.info("jedis.msetnx(key2,key3): " + jedis.msetnx("key2", "exists1", "key3", "exists2"));
  51. Logger.info("jedis.mget(key2,key3): " + jedis.mget("key2", "key3"));
  52. //设置key 2 秒后失效
  53. jedis.setex("key4", 2, "2 seconds is no Val");
  54. try {
  55. Thread.sleep(3000);
  56. } catch (InterruptedException e) {
  57. e.printStackTrace();
  58. }
  59. // 2 seconds is no Val
  60. Logger.info("jedis.get(key4): " + jedis.get("key4"));
  61. jedis.set("key6", "123456789");
  62. //下标从0开始,从第三位开始,将新值覆盖旧值
  63. jedis.setrange("key6", 3, "abcdefg");
  64. //返回:123abcdefg
  65. Logger.info("jedis.get(key6): " + jedis.get("key6"));
  66. //返回所有匹配的key
  67. Logger.info("jedis.get(key*): " + jedis.keys("key*"));
  68. jedis.close();
  69. }

14.3.2 Jedis操作List列表

  1. @Test
  2. public void operateList() {
  3. Jedis jedis = new Jedis("localhost");
  4. Logger.info("jedis.ping(): " +jedis.ping());
  5. jedis.del("list1");
  6. //从list尾部添加3个元素
  7. jedis.rpush("list1", "zhangsan", "lisi", "wangwu");
  8. //取得类型, list
  9. Logger.info("jedis.type(): " +jedis.type("list1"));
  10. //遍历区间[0,-1],取得全部的元素
  11. Logger.info("jedis.lrange(0,-1): " +jedis.lrange("list1", 0, -1));
  12. //遍历区间[1,2],取得区间的元素
  13. Logger.info("jedis.lrange(1,2): " +jedis.lrange("list1", 1, 2));
  14. //获取list长度
  15. Logger.info("jedis.llen(list1): " +jedis.llen("list1"));
  16. //获取下标为 1 的元素
  17. Logger.info("jedis.lindex(list1,1): " +jedis.lindex("list1", 1));
  18. //左侧弹出元素
  19. Logger.info("jedis.lpop(): " +jedis.lpop("list1"));
  20. //右侧弹出元素
  21. Logger.info("jedis.rpop(): " +jedis.rpop("list1"));
  22. //设置下标为0的元素val
  23. jedis.lset("list1", 0, "lisi2");
  24. //最后,遍历区间[0,-1],取得全部的元素
  25. Logger.info("jedis.lrange(0,-1): " +jedis.lrange("list1", 0, -1));
  26. jedis.close();
  27. }

14.3.3 Jedis操作Hash哈希表

  1. @Test
  2. public void operateHash() {
  3. Jedis jedis = new Jedis("localhost");
  4. jedis.del("config");
  5. //设置hash的 field-value 对
  6. jedis.hset("config", "ip", "127.0.0.1");
  7. //取得hash的 field的关联的value值
  8. Logger.info("jedis.hget(): " + jedis.hget("config", "ip"));
  9. //取得类型:hash
  10. Logger.info("jedis.type(): " + jedis.type("config"));
  11. //批量添加 field-value 对,参数为java map
  12. Map<String, String> configFields = new HashMap<String, String>();
  13. configFields.put("port", "8080");
  14. configFields.put("maxalive", "3600");
  15. configFields.put("weight", "1.0");
  16. //执行批量添加
  17. jedis.hmset("config", configFields);
  18. //批量获取:取得全部 field-value 对,返回 java map
  19. Logger.info("jedis.hgetAll(): " + jedis.hgetAll("config"));
  20. //批量获取:取得部分 field对应的value,返回 java map
  21. Logger.info("jedis.hmget(): " + jedis.hmget("config", "ip", "port"));
  22. //浮点数增加: 类似于String的 incrByFloat
  23. jedis.hincrByFloat("config", "weight", 1.2);
  24. Logger.info("jedis.hget(weight): " + jedis.hget("config", "weight"));
  25. //获取所有的key
  26. Logger.info("jedis.hkeys(config): " + jedis.hkeys("config"));
  27. //获取所有的val
  28. Logger.info("jedis.hvals(config): " + jedis.hvals("config"));
  29. //获取长度
  30. Logger.info("jedis.hlen(): " + jedis.hlen("config"));
  31. //判断field是否存在
  32. Logger.info("jedis.hexists(weight): " + jedis.hexists("config", "weight"));
  33. //删除一个field
  34. jedis.hdel("config", "weight");
  35. Logger.info("jedis.hexists(weight): " + jedis.hexists("config", "weight"));
  36. jedis.close();
  37. }

14.3.4 Jedis操作Set集合

  1. @Test
  2. public void operateSet() {
  3. Jedis jedis = new Jedis("localhost");
  4. jedis.del("set1");
  5. Logger.info("jedis.ping(): " + jedis.ping());
  6. Logger.info("jedis.type(): " + jedis.type("set1"));
  7. //sadd函数: 向集合添加元素
  8. jedis.sadd("set1", "user01", "user02", "user03");
  9. //smembers函数: 遍历所有元素
  10. Logger.info("jedis.smembers(): " + jedis.smembers("set1"));
  11. //scard函数: 获取集合元素个数
  12. Logger.info("jedis.scard(): " + jedis.scard("set1"));
  13. //sismember 判断是否是集合元素
  14. Logger.info("jedis.sismember(user04): " + jedis.sismember("set1", "user04"));
  15. //srem函数:移除元素
  16. Logger.info("jedis.srem(): " + jedis.srem("set1", "user02", "user01"));
  17. //smembers函数: 遍历所有元素
  18. Logger.info("jedis.smembers(): " + jedis.smembers("set1"));
  19. jedis.close();
  20. }

14.3.5 Jedis操作Zset有序集合

  1. @Test
  2. public void operateZset() {
  3. Jedis jedis = new Jedis("localhost");
  4. Logger.info("jedis.get(): " + jedis.ping());
  5. jedis.del("salary");
  6. Map<String, Double> members = new HashMap<String, Double>();
  7. members.put("u01", 1000.0);
  8. members.put("u02", 2000.0);
  9. members.put("u03", 3000.0);
  10. members.put("u04", 13000.0);
  11. members.put("u05", 23000.0);
  12. //批量添加元素
  13. jedis.zadd("salary", members);
  14. //类型,zset
  15. Logger.info("jedis.type(): " + jedis.type("salary"));
  16. //获取集合元素个数
  17. Logger.info("jedis.zcard(): " + jedis.zcard("salary"));
  18. //按照下标[起,止]遍历元素
  19. Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1));
  20. //按照下标[起,止]倒序遍历元素
  21. Logger.info("jedis.zrevrange(): " + jedis.zrevrange("salary", 0, -1));
  22. //按照分数(薪资)[起,止]遍历元素
  23. Logger.info("jedis.zrangeByScore(): " + jedis.zrangeByScore("salary", 1000, 10000));
  24. //按照薪资[起,止]遍历元素,带分数返回
  25. Set<Tuple> res0 = jedis.zrangeByScoreWithScores("salary", 1000, 10000);
  26. for (Tuple temp : res0) {
  27. Logger.info("Tuple.get(): " + temp.getElement() + " -> " + temp.getScore());
  28. }
  29. //按照分数[起,止]倒序遍历元素
  30. Logger.info("jedis.zrevrangeByScore(): " + jedis.zrevrangeByScore("salary", 1000, 4000));
  31. //获取元素[起,止]分数区间的元素数量
  32. Logger.info("jedis.zcount(): " + jedis.zcount("salary", 1000, 4000));
  33. //获取元素score值:薪资
  34. Logger.info("jedis.zscore(): " + jedis.zscore("salary", "u01"));
  35. //获取元素下标
  36. Logger.info("jedis.zrank(u01): " + jedis.zrank("salary", "u01"));
  37. //倒序获取元素下标
  38. Logger.info("jedis.zrevrank(u01): " + jedis.zrevrank("salary", "u01"));
  39. //删除元素
  40. Logger.info("jedis.zrem(): " + jedis.zrem("salary", "u01", "u02"));
  41. //删除元素,通过下标范围
  42. Logger.info("jedis.zremrangeByRank(): " + jedis.zremrangeByRank("salary", 0, 1));
  43. //删除元素,通过分数范围
  44. Logger.info("jedis.zremrangeByScore(): " + jedis.zremrangeByScore("salary", 20000, 30000));
  45. //按照下标[起,止]遍历元素
  46. Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1));
  47. Map<String, Double> members2 = new HashMap<String, Double>();
  48. members2.put("u11", 1136.0);
  49. members2.put("u12", 2212.0);
  50. members2.put("u13", 3324.0);
  51. //批量添加元素
  52. jedis.zadd("salary", members2);
  53. //增加指定分数
  54. Logger.info("jedis.zincrby(10000): " + jedis.zincrby("salary", 10000, "u13"));
  55. //按照下标[起,止]遍历元素
  56. Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1));
  57. jedis.close();
  58. }

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创建和预热

  1. private static JedisPool buildPool() {
  2. if (pool == null) {
  3. long start = System.currentTimeMillis();
  4. JedisPoolConfig config = new JedisPoolConfig();
  5. config.setMaxIdle(MAX_IDLE);
  6. config.setMaxTotal(MAX_TOTAL);
  7. config.setMaxWaitMillis(1000 * 10);
  8. // 在 borrow 一个 jedis 实例时,是否提前进行有效检测操作;
  9. // 如果为 true,则得到的 jedis 实例均是可用的;
  10. config.setTestOnBorrow(true);
  11. pool = new JedisPool(config, "101.132.251.198", 6379, 10000);
  12. long end = System.currentTimeMillis();
  13. Logger.info("buildPool 毫秒数:", end - start);
  14. }
  15. return pool;
  16. }

预热:

  1. public static void hotPool() {
  2. long start = System.currentTimeMillis();
  3. List<Jedis> minIdleJedisList = new ArrayList<>(MAX_IDLE);
  4. Jedis jedis = null;
  5. for (int i = 0; i < MAX_IDLE; i++) {
  6. try {
  7. jedis = pool.getResource();
  8. minIdleJedisList.add(jedis);
  9. jedis.ping();
  10. }catch (Exception e) {
  11. Logger.error(e.getMessage());
  12. }
  13. }
  14. for (int i = 0; i < MAX_IDLE; i++) {
  15. try {
  16. jedis = minIdleJedisList.get(i);
  17. jedis.close();
  18. }catch (Exception e) {
  19. Logger.error(e.getMessage());
  20. }
  21. }
  22. long end = System.currentTimeMillis();
  23. Logger.info("hotPool 毫秒数:", end - start);
  24. }

创建好连接池实例,并且进行预热

  1. static {
  2. buildPool();
  3. hotPool();
  4. }
  5. public synchronized static Jedis getJedis() {
  6. return pool.getResource();
  7. }

14.4.3 JedisPool的使用

  1. @Test
  2. public void testDel() {
  3. Jedis redis = null;
  4. try {
  5. redis = JedisPoolBuilder.getJedis();
  6. long start = System.currentTimeMillis();
  7. redis.del(ZSET_KEY);
  8. long end = System.currentTimeMillis();
  9. log.info("删除 zset1 毫秒数:", end - start);
  10. }finally {
  11. //使用后一定关闭,还给连接池
  12. if (redis != null) {
  13. redis.close();
  14. }
  15. }
  16. }

使用try-with-resources语句,在其隐藏的finally部分自动调用close方法。

  1. @Test
  2. public void testSet() {
  3. testDel();
  4. try (Jedis redis = JedisPoolBuilder.getJedis()){
  5. int loop = 0;
  6. long start = System.currentTimeMillis();
  7. while (loop < NUM) {
  8. redis.zadd(ZSET_KEY, loop, "field-" + loop);
  9. loop++;
  10. }
  11. long end = System.currentTimeMillis();
  12. Logger.info("设置 Zset :", loop, "次, 毫秒数:", end - start);
  13. }
  14. }

14.5 使用spring-data-redis完成CRUD的实践案例

  1. <dependency>
  2. <groupId>org.springframework.data</groupId>
  3. <artifactId>spring-data-redis</artifactId>
  4. <version>${springboot}</version>
  5. </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集合。

在实际开发中,为了尽可能地减少对特定的第三方库的依赖,减少第三方库的“入侵”性,或者为了在不同的第三方库之间进行方便的切换,一般来说,要对第三方库进行 封装

  1. public class CacheOperationService {
  2. private RedisTemplate redisTemplate;
  3. public void setRedisTemplate(RedisTemplate redisTemplate) {
  4. this.redisTemplate = redisTemplate;
  5. }
  6. // --------------RedisTemplate 基础操作 --------------------
  7. /**
  8. * 取得指定格式的所有的key
  9. *
  10. * @param patens 匹配的表达式
  11. * @return key 的集合
  12. */
  13. public Set getKeys(Object patens) {
  14. try {
  15. return redisTemplate.keys(patens);
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. return null;
  19. }
  20. }
  21. /**
  22. * 指定缓存失效时间
  23. *
  24. * @param key 键
  25. * @param time 时间(秒)
  26. * @return
  27. */
  28. public boolean expire(String key, long time) {
  29. try {
  30. if (time > 0) {
  31. redisTemplate.expire(key, time, TimeUnit.SECONDS);
  32. }
  33. return true;
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. return false;
  37. }
  38. }
  39. /**
  40. * 根据key 获取过期时间
  41. *
  42. * @param key 键 不能为null
  43. * @return 时间(秒) 返回0代表为永久有效
  44. */
  45. public long getExpire(String key) {
  46. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  47. }
  48. /**
  49. * 判断key是否存在
  50. *
  51. * @param key 键
  52. * @return true 存在 false不存在
  53. */
  54. public boolean hasKey(String key) {
  55. try {
  56. return redisTemplate.hasKey(key);
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. return false;
  60. }
  61. }
  62. /**
  63. * 删除缓存
  64. *
  65. * @param key 可以传一个值 或多个
  66. * @return 删除的个数
  67. */
  68. @SuppressWarnings("unchecked")
  69. public void del(String... key) {
  70. if (key != null && key.length > 0) {
  71. if (key.length == 1) {
  72. redisTemplate.delete(key[0]);
  73. } else {
  74. redisTemplate.delete(CollectionUtils.arrayToList(key));
  75. }
  76. }
  77. }
  78. // --------------RedisTemplate 操作 String --------------------
  79. /**
  80. * 普通缓存获取
  81. *
  82. * @param key 键
  83. * @return 值
  84. */
  85. public Object get(String key) {
  86. return key == null ? null : redisTemplate.opsForValue().get(key);
  87. }
  88. /**
  89. * 普通缓存放入
  90. *
  91. * @param key 键
  92. * @param value 值
  93. * @return true成功 false失败
  94. */
  95. public boolean set(String key, Object value) {
  96. try {
  97. redisTemplate.opsForValue().set(key, value);
  98. return true;
  99. } catch (Exception e) {
  100. e.printStackTrace();
  101. return false;
  102. }
  103. }
  104. /**
  105. * 普通缓存放入并设置时间
  106. *
  107. * @param key 键
  108. * @param value 值
  109. * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  110. * @return true成功 false 失败
  111. */
  112. public boolean set(String key, Object value, long time) {
  113. try {
  114. if (time > 0) {
  115. redisTemplate.opsForValue()
  116. .set(key, value, time, TimeUnit.SECONDS);
  117. } else {
  118. set(key, value);
  119. }
  120. return true;
  121. } catch (Exception e) {
  122. e.printStackTrace();
  123. return false;
  124. }
  125. }
  126. /**
  127. * 递增
  128. *
  129. * @param key 键
  130. * @param delta 自增因子,要增加几(大于0)
  131. * @return 自增的结果
  132. */
  133. public long incr(String key, long delta) {
  134. if (delta < 0) {
  135. throw new RuntimeException("自增因子必须大于0");
  136. }
  137. return redisTemplate.opsForValue().increment(key, delta);
  138. }
  139. /**
  140. * 自减
  141. *
  142. * @param key 键
  143. * @param delta 自减, 要减少几(小于0)
  144. * @return
  145. */
  146. public long decr(String key, long delta) {
  147. if (delta < 0) {
  148. throw new RuntimeException("自减因子必须大于0");
  149. }
  150. return redisTemplate
  151. .opsForValue().increment(key, -delta);
  152. }
  153. // --------------RedisTemplate 操作 Map --------------------
  154. /**
  155. * HashGet
  156. *
  157. * @param key 键 不能为null
  158. * @param field 项 不能为null
  159. * @return 值
  160. */
  161. public Object hget(String key, String field) {
  162. return redisTemplate.opsForHash().get(key, field);
  163. }
  164. /**
  165. * 获取hashKey对应的所有键值
  166. *
  167. * @param key 键
  168. * @return 对应的多个键值
  169. */
  170. public Map<Object, Object> hmget(String key) {
  171. return redisTemplate.opsForHash().entries(key);
  172. }
  173. /**
  174. * HashSet
  175. *
  176. * @param key 键
  177. * @param map 对应多个键值
  178. * @return true 成功 false 失败
  179. */
  180. public boolean hmset(String key, Map<String, Object> map) {
  181. try {
  182. redisTemplate.opsForHash().putAll(key, map);
  183. return true;
  184. } catch (Exception e) {
  185. e.printStackTrace();
  186. return false;
  187. }
  188. }
  189. /**
  190. * HashSet 并设置时间
  191. *
  192. * @param key 键
  193. * @param map 对应多个键值
  194. * @param time 时间(秒)
  195. * @return true成功 false失败
  196. */
  197. public boolean hmset(String key, Map<String, Object> map, long time) {
  198. try {
  199. redisTemplate.opsForHash().putAll(key, map);
  200. if (time > 0) {
  201. expire(key, time);
  202. }
  203. return true;
  204. } catch (Exception e) {
  205. e.printStackTrace();
  206. return false;
  207. }
  208. }
  209. /**
  210. * 向一张hash表中放入数据,如果不存在将创建
  211. *
  212. * @param key 键
  213. * @param field 项
  214. * @param value 值
  215. * @return true 成功 false失败
  216. */
  217. public boolean hset(String key, String field, Object value) {
  218. try {
  219. redisTemplate.opsForHash().put(key, field, value);
  220. return true;
  221. } catch (Exception e) {
  222. e.printStackTrace();
  223. return false;
  224. }
  225. }
  226. /**
  227. * 向一张hash表中放入数据,如果不存在将创建
  228. *
  229. * @param key 键
  230. * @param field 项
  231. * @param value 值
  232. * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
  233. * @return true 成功 false失败
  234. */
  235. public boolean hset(String key, String field, Object value, long time) {
  236. try {
  237. redisTemplate.opsForHash().put(key, field, value);
  238. if (time > 0) {
  239. expire(key, time);
  240. }
  241. return true;
  242. } catch (Exception e) {
  243. e.printStackTrace();
  244. return false;
  245. }
  246. }
  247. /**
  248. * 删除hash表中的值
  249. *
  250. * @param key 键 不能为null
  251. * @param field 项 可以使多个 不能为null
  252. */
  253. public void hdel(String key, Object... field) {
  254. redisTemplate.opsForHash().delete(key, field);
  255. }
  256. /**
  257. * 判断hash表中是否有该项的值
  258. *
  259. * @param key 键 不能为null
  260. * @param field 项 不能为null
  261. * @return true 存在 false不存在
  262. */
  263. public boolean hHasKey(String key, String field) {
  264. return redisTemplate.opsForHash().hasKey(key, field);
  265. }
  266. /**
  267. * hash自增 如果不存在,就会创建一个 并把新增后的值返回
  268. *
  269. * @param key 键
  270. * @param field 项
  271. * @param by 要增加几(大于0)
  272. * @return
  273. */
  274. public double hincr(String key, String field, double by) {
  275. return redisTemplate.opsForHash().increment(key, field, by);
  276. }
  277. /**
  278. * hash自减
  279. *
  280. * @param key 键
  281. * @param field 项
  282. * @param by 要减少记(小于0)
  283. * @return
  284. */
  285. public double hdecr(String key, String field, double by) {
  286. return redisTemplate.opsForHash().increment(key, field, -by);
  287. }
  288. // --------------RedisTemplate 操作 Set --------------------
  289. /**
  290. * 根据key获取Set中的所有值
  291. *
  292. * @param key 键
  293. * @return
  294. */
  295. public Set<Object> sGet(String key) {
  296. try {
  297. return redisTemplate.opsForSet().members(key);
  298. } catch (Exception e) {
  299. e.printStackTrace();
  300. return null;
  301. }
  302. }
  303. /**
  304. * 根据value从一个set中查询,是否存在
  305. *
  306. * @param key 键
  307. * @param value 值
  308. * @return true 存在 false不存在
  309. */
  310. public boolean sHasKey(String key, Object value) {
  311. try {
  312. return redisTemplate.opsForSet().isMember(key, value);
  313. } catch (Exception e) {
  314. e.printStackTrace();
  315. return false;
  316. }
  317. }
  318. /**
  319. * 将数据放入set缓存
  320. *
  321. * @param key 键
  322. * @param values 值 可以是多个
  323. * @return 成功个数
  324. */
  325. public long sSet(String key, Object... values) {
  326. try {
  327. return redisTemplate.opsForSet().add(key, values);
  328. } catch (Exception e) {
  329. e.printStackTrace();
  330. return 0;
  331. }
  332. }
  333. /**
  334. * 将set数据放入缓存
  335. *
  336. * @param key 键
  337. * @param time 时间(秒)
  338. * @param values 值 可以是多个
  339. * @return 成功个数
  340. */
  341. public long sSetAndTime(String key, long time, Object... values) {
  342. try {
  343. Long count = redisTemplate.opsForSet().add(key, values);
  344. if (time > 0)
  345. expire(key, time);
  346. return count;
  347. } catch (Exception e) {
  348. e.printStackTrace();
  349. return 0;
  350. }
  351. }
  352. /**
  353. * 获取set缓存的长度
  354. *
  355. * @param key 键
  356. * @return
  357. */
  358. public long sGetSetSize(String key) {
  359. try {
  360. return redisTemplate.opsForSet().size(key);
  361. } catch (Exception e) {
  362. e.printStackTrace();
  363. return 0;
  364. }
  365. }
  366. /**
  367. * 移除值为value的
  368. *
  369. * @param key 键
  370. * @param values 值 可以是多个
  371. * @return 移除的个数
  372. */
  373. public long setRemove(String key, Object... values) {
  374. try {
  375. Long count = redisTemplate.opsForSet().remove(key, values);
  376. return count;
  377. } catch (Exception e) {
  378. e.printStackTrace();
  379. return 0;
  380. }
  381. }
  382. // --------------RedisTemplate 操作list --------------------
  383. /**
  384. * 获取list缓存的内容
  385. *
  386. * @param key 键
  387. * @param start 开始
  388. * @param end 结束 0 到 -1代表所有值
  389. * @return
  390. */
  391. public List<Object> lGet(String key, long start, long end) {
  392. try {
  393. return redisTemplate.opsForList().range(key, start, end);
  394. } catch (Exception e) {
  395. e.printStackTrace();
  396. return null;
  397. }
  398. }
  399. /**
  400. * 获取list缓存的长度
  401. *
  402. * @param key 键
  403. * @return
  404. */
  405. public long lGetListSize(String key) {
  406. try {
  407. return redisTemplate.opsForList().size(key);
  408. } catch (Exception e) {
  409. e.printStackTrace();
  410. return 0;
  411. }
  412. }
  413. /**
  414. * 通过索引 获取list中的值
  415. *
  416. * @param key 键
  417. * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
  418. * @return
  419. */
  420. public Object lGetIndex(String key, long index) {
  421. try {
  422. return redisTemplate.opsForList().index(key, index);
  423. } catch (Exception e) {
  424. e.printStackTrace();
  425. return null;
  426. }
  427. }
  428. /**
  429. * 将list放入缓存
  430. *
  431. * @param key 键
  432. * @param value 值
  433. * @return
  434. */
  435. public boolean lSet(String key, Object value) {
  436. try {
  437. redisTemplate.opsForList().rightPush(key, value);
  438. return true;
  439. } catch (Exception e) {
  440. e.printStackTrace();
  441. return false;
  442. }
  443. }
  444. /**
  445. * 将list放入缓存
  446. *
  447. * @param key 键
  448. * @param value 值
  449. * @param time 时间(秒)
  450. * @return
  451. */
  452. public boolean lSet(String key, Object value, long time) {
  453. try {
  454. redisTemplate.opsForList().rightPush(key, value);
  455. if (time > 0)
  456. expire(key, time);
  457. return true;
  458. } catch (Exception e) {
  459. e.printStackTrace();
  460. return false;
  461. }
  462. }
  463. /**
  464. * 将list放入缓存
  465. *
  466. * @param key 键
  467. * @param value 值
  468. * @return
  469. */
  470. public boolean lSet(String key, List<Object> value) {
  471. try {
  472. redisTemplate.opsForList().rightPushAll(key, value);
  473. return true;
  474. } catch (Exception e) {
  475. e.printStackTrace();
  476. return false;
  477. }
  478. }
  479. /**
  480. * 将list放入缓存
  481. *
  482. * @param key 键
  483. * @param value 值
  484. * @param time 时间(秒)
  485. * @return
  486. */
  487. public boolean lSet(String key, List<Object> value, long time) {
  488. try {
  489. redisTemplate.opsForList().rightPushAll(key, value);
  490. if (time > 0)
  491. expire(key, time);
  492. return true;
  493. } catch (Exception e) {
  494. e.printStackTrace();
  495. return false;
  496. }
  497. }
  498. /**
  499. * 根据索引修改list中的某条数据
  500. *
  501. * @param key 键
  502. * @param index 索引
  503. * @param value 值
  504. * @return
  505. */
  506. public boolean lUpdateIndex(String key, long index, Object value) {
  507. try {
  508. redisTemplate.opsForList().set(key, index, value);
  509. return true;
  510. } catch (Exception e) {
  511. e.printStackTrace();
  512. return false;
  513. }
  514. }
  515. /**
  516. * 移除N个值为value
  517. *
  518. * @param key 键
  519. * @param count 移除多少个
  520. * @param value 值
  521. * @return 移除的个数
  522. */
  523. public long lRemove(String key, long count, Object value) {
  524. try {
  525. Long remove = redisTemplate.opsForList().remove(key, count, value);
  526. return remove;
  527. } catch (Exception e) {
  528. e.printStackTrace();
  529. return 0;
  530. }
  531. }
  532. }

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的实践案例

  1. public class UserServiceImpleWithTemplate implements UserService{
  2. public static final String USER_UID_PREFIX = "user:uid:";
  3. protected CacheOperationService cacheOperationService;
  4. private static final long CASHE_LONG = 60 * 4;
  5. @Override
  6. public User saveUser(User user) {
  7. String key = USER_UID_PREFIX + user.getUid();
  8. Logger.info("user:", user);
  9. cacheOperationService.set(key, user, CASHE_LONG);
  10. return user;
  11. }
  12. @Override
  13. public User getUser(long id) {
  14. String key = USER_UID_PREFIX + id;
  15. User value = (User) cacheOperationService.get(key);
  16. if (null == value) {
  17. return null;
  18. }
  19. return value;
  20. }
  21. @Override
  22. public void deleteUser(long id) {
  23. String key = USER_UID_PREFIX + id;
  24. cacheOperationService.del(key);
  25. Logger.info("delete User: ", id);
  26. }
  27. }

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的实践案例

  1. @CachePut作用是设置缓存。先执行方法,并将执行结果缓存起来。
  2. @CacheEvict的作用是删除缓存。在执行方法后,删除缓存。
  3. @Cacheable的作用更多是查询缓存。首先检查注解中的Key键是否在缓存中,如果是,则返回Key的缓存值,不再执行方法;否则,执行方法并将方法结果缓存起来。从后半部分来看, @Cacheable也具备@CachePut的能力。

    1. public class UserServiceImplWithAnno implements UserService{
    2. public static final String USER_UID_PREFIX = "'userCache:'+";
    3. @CachePut(key = USER_UID_PREFIX + "T(String).valueOf(#user.uid)")
    4. @Override
    5. public User saveUser(User user) {
    6. Logger.info("user: save to redis");
    7. return user;
    8. }
    9. @Cacheable(key = USER_UID_PREFIX + "T(String).valueOf(#id)")
    10. @Override
    11. public User getUser(long id) {
    12. //如果缓存没有,则从数据库中加载
    13. Logger.info("user: is null");
    14. return null;
    15. }
    16. @CacheEvict(key = USER_UID_PREFIX + "T(String).valueOf(#id)")
    17. @Override
    18. public void deleteUser(long id) {
    19. Logger.info("delete User:", id);
    20. }
    21. }

    14.6.3 详解@CachePut@Cacheable注解

  4. @CachePut负责增加缓存。

  5. @Cacheable负责查询缓存,如果没有查到, 才去执行被注解的方法,并将方法
    的结果增加到缓存。

    14.6.4 详解@CacheEvict注解

    注解@CacheEvict主要用来清除缓存,可以指定的属性有value、 key、 condition、allEntries和beforeInvocation。其中value、 key和condition的语义与@Cacheable对应的属性类似。 value表示清除哪些Cache(对应Cache的空间名称); key表示清除哪个Key键;condition表示清除的条件。

  6. allEntries属性:表示是否全部清空

  7. beforeInvocation属性:表示是否在方法执行前操作缓存

    14.6.5 详解@Caching组合注解

    @Caching注解,是一个缓存处理的组合注解。通过@Caching,可以一次指定多个Spring Cache注解的组合。 @Caching注解拥有三个属性:cacheable、 put和evict。

  8. cacheable属性:用于指定一个或者多个@Cacheable注解的组合,可以指定一个,也可以指定多个。

  9. put属性:用于指定一个或者多个@CachePut注解的组合,可以指定一个,也可以指定多个,用于设置一个或多个key的缓存。
  10. evict属性:用于指定一个或者多个@CacheEvict注解的组合,可以指定一个,也可以指定多个,用于删除一个或多个key的缓存。

    14.7 详解SpringEL(SpEL)

    Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”。 SpEL提供一种强大、简洁的Spring Bean的动态操作表达式。