13

SpringBoot2.4.x整合Redis(RedisTemplate操作五大常用数据类型)

 3 years ago
source link: https://maxqiu.com/article/detail/102
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

示例代码:
GitHub:https://github.com/Max-Qiu/demo-SpringBoot
Gitee:https://gitee.com/Max-Qiu/demo-SpringBoot


官方文档:Spring Data Redis

之前一篇文章SpringBoot2.4.x缓存介绍以及整合Redis仅介绍了如何使用Redis作为缓存。

如果想要直接操作Redis,SpringBoot提供了RedisTemplate类用来直接操作数据

POM依赖

spring-boot-starter-data-redis使用的Redis客户端已经从jedis更改为lettuce

  1. <!-- Redis -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-redis</artifactId>
  5. </dependency>
  6. <!-- Jackson格式化 -->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-json</artifactId>
  10. </dependency>
  11. <!-- 连接池依赖 -->
  12. <dependency>
  13. <groupId>org.apache.commons</groupId>
  14. <artifactId>commons-pool2</artifactId>
  15. </dependency>

yml配置文件

根据Redis服务的模式不同,对应的配置文件也不同

  1. spring:
  2. redis:
  3. ## 单机模式
  4. host: 192.168.220.101 # 地址
  5. port: 6379 # 端口
  6. # 通用配置
  7. username: # 用户名
  8. password: 123 # 密码
  9. database: 0 # 指定数据库序号
  10. ssl: false # 是否启用SSL
  11. connect-timeout: 1000 # 连接超时时间(毫秒)
  12. timeout: 1000 # 操作超时时间(毫秒)
  13. client-name: # 客户端名称(不知道干嘛用的)
  14. client-type: lettuce # 驱动类型
  15. # 连接池配置
  16. lettuce:
  17. pool:
  18. min-idle: 1 # 最小空闲连接(默认0)
  19. max-idle: 8 # 最大空闲连接(默认8)
  20. max-active: 16 # 最大连接数(默认8,使用负值表示没有限制)
  21. max-wait: -1ms # 最大阻塞等待时间(默认-1,负数表示没限制)

哨兵模式(主从复制)

  1. spring:
  2. redis:
  3. ## 哨兵模式(主从复制)
  4. sentinel:
  5. master: master # 哨兵的sentinel.conf配置文件中的主节点名称
  6. password: 123 # 哨兵的密码
  7. nodes: 192.168.220.101:26379,192.168.220.102:26379,192.168.220.103:26379 # 哨兵节点
  8. # 通用配置
  9. username: # 用户名
  10. password: 123 # 密码
  11. database: 0 # 指定数据库序号
  12. ssl: false # 是否启用SSL
  13. connect-timeout: 1000 # 连接超时时间(毫秒)
  14. timeout: 1000 # 操作超时时间(毫秒)
  15. client-name: # 客户端名称(不知道干嘛用的)
  16. client-type: lettuce # 驱动类型
  17. # 连接池配置
  18. lettuce:
  19. pool:
  20. min-idle: 1 # 最小空闲连接(默认0)
  21. max-idle: 8 # 最大空闲连接(默认8)
  22. max-active: 16 # 最大连接数(默认8,使用负值表示没有限制)
  23. max-wait: -1ms # 最大阻塞等待时间(默认-1,负数表示没限制)
  1. spring:
  2. redis:
  3. ## 集群模式
  4. cluster:
  5. nodes: 192.168.220.101:6379,192.168.220.102:6379,192.168.220.103:6379,192.168.220.101:6380,192.168.220.102:6380,192.168.220.103:6380
  6. # 通用配置
  7. username: # 用户名
  8. password: 123 # 密码
  9. database: 0 # 指定数据库序号
  10. ssl: false # 是否启用SSL
  11. connect-timeout: 1000 # 连接超时时间(毫秒)
  12. timeout: 1000 # 操作超时时间(毫秒)
  13. client-name: # 客户端名称(不知道干嘛用的)
  14. client-type: lettuce # 驱动类型
  15. # 连接池配置
  16. lettuce:
  17. pool:
  18. min-idle: 1 # 最小空闲连接(默认0)
  19. max-idle: 8 # 最大空闲连接(默认8)
  20. max-active: 16 # 最大连接数(默认8,使用负值表示没有限制)
  21. max-wait: -1ms # 最大阻塞等待时间(默认-1,负数表示没限制)
  22. cluster:
  23. refresh:
  24. period: 1000 # 集群模式!需要设置群集拓扑刷新周期(毫秒)

RedisConfig配置文件自定义RedisTemplate

默认情况下,org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration会生成一个默认的RedisTemplate,且序列化时使用JdkSerializationRedisSerializer,详见org.springframework.data.redis.core.RedisTemplate

然而Jdk的序列化在Redis中可读性不好,我们需要自定义Json格式的序列化转换,示例中的RedisSerializer.json()就是GenericJackson2JsonRedisSerializer

但是Jackson在格式化String字符串时会在两边加上",例如hello变成"hello",所以RedisAutoConfiguration同时还提供了一个StringRedisTemplate,详见org.springframework.data.redis.core.StringRedisTemplate

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.data.redis.connection.RedisConnectionFactory;
  4. import org.springframework.data.redis.core.RedisTemplate;
  5. import org.springframework.data.redis.serializer.RedisSerializer;
  6. /**
  7. * Redis配置项
  8. */
  9. @Configuration
  10. public class RedisConfig {
  11. /**
  12. * 自定义RedisTemplate,使用json格式化value
  13. */
  14. @Bean
  15. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  16. RedisTemplate<String, Object> template = new RedisTemplate<>();
  17. // 连接工厂
  18. template.setConnectionFactory(redisConnectionFactory);
  19. // key序列化
  20. template.setKeySerializer(RedisSerializer.string());
  21. // value序列化
  22. template.setValueSerializer(RedisSerializer.json());
  23. // hash序列化
  24. template.setHashKeySerializer(RedisSerializer.string());
  25. template.setHashValueSerializer(RedisSerializer.json());
  26. // 启用事务支持
  27. template.setEnableTransactionSupport(true);
  28. return template;
  29. }
  30. }

一般情况下,有如下两个常用对象

  1. /**
  2. * RedisTemplate操作通用对象
  3. */
  4. @Autowired
  5. private RedisTemplate<String, Object> redisTemplate;
  6. /**
  7. * 操作String类型的对象
  8. */
  9. @Autowired
  10. private StringRedisTemplate stringRedisTemplate;

RedisTemplate又可以通过opsForXxx生成对应的类型的操作对象,例如

  1. // RedisTemplate 可以获取对应数据类型的 XxxOperations
  2. ValueOperations<String, Object> objectValueOperations = redisTemplate.opsForValue();
  3. ListOperations<String, Object> objectListOperations = redisTemplate.opsForList();
  4. SetOperations<String, Object> objectSetOperations = redisTemplate.opsForSet();
  5. HashOperations<String, String, Object> objectHashOperations = redisTemplate.opsForHash();
  6. ZSetOperations<String, Object> objectZSetOperations = redisTemplate.opsForZSet();

简单点,若后面要获取XxxOperations可以直接使用@Resource注解获取,且获取时可以将Object对象根据值类型换成具体的对象。注意,值是String类型时name需要使用stringRedisTemplate

  1. /**
  2. * 值是String类型的字符串
  3. *
  4. * 自定义的RedisTemplate在格式化String时会多出引号
  5. *
  6. * 需要使用Spring内置的StringRedisTemplate
  7. */
  8. @Resource(name = "stringRedisTemplate")
  9. private ValueOperations<String, String> stringValueOperations;
  10. /**
  11. * 值是对象类型的字符串
  12. *
  13. * 使用自定义的RedisTemplate,将值格式化成JSON
  14. */
  15. @Resource(name = "redisTemplate")
  16. private ValueOperations<String, User> userValueOperations;
  17. /**
  18. * 值是数字类型的字符串
  19. *
  20. * 自定义的RedisTemplate可以格式化Integer
  21. */
  22. @Resource(name = "redisTemplate")
  23. private ValueOperations<String, Integer> integerValueOperations;

RedisTemplate 基础操作

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.junit.jupiter.api.Assertions.assertFalse;
  3. import static org.junit.jupiter.api.Assertions.assertNotNull;
  4. import static org.junit.jupiter.api.Assertions.assertTrue;
  5. import java.time.Duration;
  6. import java.util.Arrays;
  7. import java.util.Set;
  8. import java.util.concurrent.TimeUnit;
  9. import org.junit.jupiter.api.Test;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.boot.test.context.SpringBootTest;
  12. import org.springframework.data.redis.connection.DataType;
  13. import org.springframework.data.redis.core.HashOperations;
  14. import org.springframework.data.redis.core.ListOperations;
  15. import org.springframework.data.redis.core.RedisTemplate;
  16. import org.springframework.data.redis.core.SetOperations;
  17. import org.springframework.data.redis.core.StringRedisTemplate;
  18. import org.springframework.data.redis.core.ValueOperations;
  19. import org.springframework.data.redis.core.ZSetOperations;
  20. /**
  21. * Keys 键相关
  22. */
  23. @SpringBootTest
  24. public class BaseOperationsTest {
  25. /**
  26. * RedisTemplate操作通用对象
  27. */
  28. @Autowired
  29. private RedisTemplate<String, Object> redisTemplate;
  30. /**
  31. * 操作String类型的对象
  32. */
  33. @Autowired
  34. private StringRedisTemplate stringRedisTemplate;
  35. @Test
  36. void test() {
  37. // RedisTemplate 可以获取对应数据类型的 XxxOperations
  38. ValueOperations<String, Object> objectValueOperations = redisTemplate.opsForValue();
  39. ListOperations<String, Object> objectListOperations = redisTemplate.opsForList();
  40. SetOperations<String, Object> objectSetOperations = redisTemplate.opsForSet();
  41. HashOperations<String, String, Object> objectHashOperations = redisTemplate.opsForHash();
  42. ZSetOperations<String, Object> objectZSetOperations = redisTemplate.opsForZSet();
  43. // 准备数据
  44. ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
  45. stringValueOperations.set("key1", "hello1");
  46. stringValueOperations.set("key2", "hello2");
  47. stringValueOperations.set("key3", "hello3");
  48. stringValueOperations.set("key4", "hello4");
  49. // KEYS 查找键
  50. Set<String> keys = redisTemplate.keys("*");
  51. assertNotNull(keys);
  52. assertEquals(4, keys.size());
  53. // EXISTS 键是否存在
  54. Boolean hasKey = redisTemplate.hasKey("key1");
  55. assertTrue(hasKey);
  56. Long existingKeys = redisTemplate.countExistingKeys(Arrays.asList("key1", "key2"));
  57. assertEquals(2, existingKeys);
  58. // TYPE 查看键的类型
  59. assertEquals(DataType.STRING, redisTemplate.type("key1"));
  60. // DEL 删除键
  61. Boolean deleteFalse = redisTemplate.delete("key");
  62. assertFalse(deleteFalse);
  63. Boolean deleteSuccess = redisTemplate.delete("key1");
  64. assertTrue(deleteSuccess);
  65. Long batchDelete = redisTemplate.delete(Arrays.asList("key", "key2"));
  66. assertEquals(1, batchDelete);
  67. // UNLINK 删除键(异步)
  68. Boolean unlink = redisTemplate.unlink("key3");
  69. assertTrue(unlink);
  70. Long batchUnlink = redisTemplate.unlink(Arrays.asList("key1", "key2"));
  71. assertEquals(0, batchUnlink);
  72. Long expire = redisTemplate.getExpire("key4");
  73. // TTL 查看键剩余生成秒
  74. assertEquals(-1, expire);
  75. // EXPIRE 设置键到期秒
  76. redisTemplate.expire("key4", 10, TimeUnit.SECONDS);
  77. redisTemplate.expire("key4", Duration.ofSeconds(10));
  78. assertEquals(10, redisTemplate.getExpire("key4"));
  79. // PERSIST key 设置键永不过期
  80. redisTemplate.persist("key4");
  81. assertEquals(-1, redisTemplate.getExpire("key4"));
  82. // 删除数据
  83. assertTrue(redisTemplate.delete("key4"));
  84. }
  85. }

ValueOperations String 字符串

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.junit.jupiter.api.Assertions.assertFalse;
  3. import static org.junit.jupiter.api.Assertions.assertNotNull;
  4. import static org.junit.jupiter.api.Assertions.assertNull;
  5. import static org.junit.jupiter.api.Assertions.assertTrue;
  6. import java.math.BigDecimal;
  7. import java.time.Duration;
  8. import java.util.Arrays;
  9. import java.util.List;
  10. import java.util.Map;
  11. import javax.annotation.Resource;
  12. import org.junit.jupiter.api.Order;
  13. import org.junit.jupiter.api.Test;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.boot.test.context.SpringBootTest;
  16. import org.springframework.data.redis.core.RedisTemplate;
  17. import org.springframework.data.redis.core.ValueOperations;
  18. import com.maxqiu.demo.entity.User;
  19. /**
  20. * String 字符串
  21. */
  22. @SpringBootTest
  23. public class StringOperationsTest {
  24. @Autowired
  25. private RedisTemplate<String, Object> redisTemplate;
  26. /**
  27. * 值是String类型的字符串
  28. *
  29. * 自定义的RedisTemplate在格式化String时会多出引号
  30. *
  31. * 需要使用Spring内置的StringRedisTemplate
  32. */
  33. @Resource(name = "stringRedisTemplate")
  34. private ValueOperations<String, String> stringValueOperations;
  35. /**
  36. * 值是对象类型的字符串
  37. *
  38. * 使用自定义的RedisTemplate,将值格式化成JSON
  39. */
  40. @Resource(name = "redisTemplate")
  41. private ValueOperations<String, User> userValueOperations;
  42. /**
  43. * 值是数字类型的字符串
  44. *
  45. * 自定义的RedisTemplate可以格式化Integer
  46. */
  47. @Resource(name = "redisTemplate")
  48. private ValueOperations<String, Integer> integerValueOperations;
  49. /**
  50. * GET
  51. */
  52. @Test
  53. @Order(1)
  54. void get() {
  55. // 获取不存在的键
  56. assertNull(stringValueOperations.get("nonexistent"));
  57. // 获取String类型的数据
  58. stringValueOperations.set("key", "hello");
  59. assertEquals("hello", stringValueOperations.get("key"));
  60. // 获取对象类型的数据
  61. User user = new User(1, "tom", new BigDecimal(18));
  62. userValueOperations.set("user", user);
  63. assertEquals(user, userValueOperations.get("user"));
  64. integerValueOperations.set("num", 1);
  65. assertEquals(1, integerValueOperations.get("num"));
  66. redisTemplate.delete(Arrays.asList("user", "key", "num"));
  67. }
  68. /**
  69. * SET
  70. *
  71. * SETEX
  72. *
  73. * SETNX
  74. *
  75. * GETSET
  76. */
  77. @Test
  78. @Order(2)
  79. void set() {
  80. /// SET 设置键与值
  81. // 简单设置值
  82. stringValueOperations.set("key1", "hello");
  83. assertEquals("hello", stringValueOperations.get("key1"));
  84. // 如果键存在才设置值
  85. Boolean flag1 = stringValueOperations.setIfPresent("key1", "world");
  86. assertTrue(flag1);
  87. assertEquals("world", stringValueOperations.get("key1"));
  88. Boolean flag2 = stringValueOperations.setIfPresent("key2", "world");
  89. assertFalse(flag2);
  90. assertNull(stringValueOperations.get("key2"));
  91. // 同上,并设置超时时间
  92. Boolean flag3 = stringValueOperations.setIfPresent("key1", "hello", Duration.ofSeconds(10));
  93. assertTrue(flag3);
  94. // stringOperations.setIfPresent("key2", "world", 10, TimeUnit.SECONDS);
  95. assertEquals(10, redisTemplate.getExpire("key1"));
  96. // SETEX 设置键与值并设置超时时间
  97. stringValueOperations.set("key2", "world", Duration.ofSeconds(10));
  98. // stringOperations.set("key2", "world", 10, TimeUnit.SECONDS);
  99. assertEquals(10, redisTemplate.getExpire("key2"));
  100. // SETNX 键不存在则设置键与值
  101. Boolean flag4 = stringValueOperations.setIfAbsent("key3", "hello");
  102. assertTrue(flag4);
  103. assertEquals("hello", stringValueOperations.get("key3"));
  104. // SET ... NX 键不存在则设置键与值(同时设置时间)
  105. Boolean flag5 = stringValueOperations.setIfAbsent("key4", "world", Duration.ofSeconds(10));
  106. // stringOperations.setIfAbsent("key4", "world", 10, TimeUnit.SECONDS);
  107. assertTrue(flag5);
  108. assertEquals("world", stringValueOperations.get("key4"));
  109. // GETSET 获取值并设置一个新值
  110. String s = stringValueOperations.getAndSet("key4", "redis");
  111. assertEquals(s, "world");
  112. assertEquals("redis", stringValueOperations.get("key4"));
  113. assertEquals(4, redisTemplate.delete(Arrays.asList("key1", "key2", "key3", "key4")));
  114. }
  115. @Test
  116. @Order(3)
  117. void other() {
  118. // APPEND 拼接字符串
  119. Integer append1 = stringValueOperations.append("key", "hello");
  120. assertEquals(5, append1);
  121. Integer append2 = stringValueOperations.append("key", " world");
  122. assertEquals(11, append2);
  123. // STRLEN 获取字符串长度
  124. Long size = stringValueOperations.size("key");
  125. assertEquals(11, size);
  126. // GETRANGE 截取字符串
  127. String a1 = stringValueOperations.get("key", 0, 3);
  128. assertEquals("hell", a1);
  129. String a2 = stringValueOperations.get("key", -3, -1);
  130. assertEquals("rld", a2);
  131. String a3 = stringValueOperations.get("key", 0, -1);
  132. assertEquals("hello world", a3);
  133. String a4 = stringValueOperations.get("key", 20, 100);
  134. assertEquals("", a4);
  135. // SETRANGE 修改字符串
  136. stringValueOperations.set("key", "redis", 6);
  137. assertEquals("hello redis", stringValueOperations.get("key"));
  138. // MSET 批量设置值
  139. stringValueOperations.multiSet(Map.of("key1", "v1", "key2", "v2"));
  140. assertTrue(redisTemplate.hasKey("key1"));
  141. // MGET 批量获取值
  142. List<String> list = stringValueOperations.multiGet(Arrays.asList("key1", "key2"));
  143. assertNotNull(list);
  144. assertEquals(2, list.size());
  145. // MSETNX 批量设置值(仅当键不存在)
  146. Boolean flag = stringValueOperations.multiSetIfAbsent(Map.of("key1", "v1", "key3", "v3"));
  147. assertFalse(flag);
  148. integerValueOperations.set("num", 1);
  149. // INCR 加一
  150. Long num1 = integerValueOperations.increment("num");
  151. assertEquals(2, num1);
  152. // DECR 减一
  153. Long num2 = integerValueOperations.decrement("num");
  154. assertEquals(1, num2);
  155. // INCRBY 加N
  156. Long num3 = integerValueOperations.increment("num", 15);
  157. assertEquals(16, num3);
  158. // DECRBY 减N
  159. Long num4 = integerValueOperations.decrement("num", 6);
  160. assertEquals(10, num4);
  161. redisTemplate.delete(Arrays.asList("key", "key1", "key2", "num"));
  162. }
  163. }

ListOperations List 列表

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.junit.jupiter.api.Assertions.assertNotNull;
  3. import java.math.BigDecimal;
  4. import java.util.Arrays;
  5. import java.util.List;
  6. import javax.annotation.Resource;
  7. import org.junit.jupiter.api.Order;
  8. import org.junit.jupiter.api.Test;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.boot.test.context.SpringBootTest;
  11. import org.springframework.data.redis.core.ListOperations;
  12. import org.springframework.data.redis.core.RedisTemplate;
  13. import com.maxqiu.demo.entity.User;
  14. /**
  15. * List 列表
  16. */
  17. @SpringBootTest
  18. public class ListOperationsTest {
  19. @Autowired
  20. private RedisTemplate<String, Object> redisTemplate;
  21. /**
  22. * 值是String类型的列表
  23. *
  24. * 自定义的RedisTemplate在格式化String时会多出引号
  25. *
  26. * 需要使用Spring内置的StringRedisTemplate
  27. */
  28. @Resource(name = "stringRedisTemplate")
  29. private ListOperations<String, String> stringListOperations;
  30. /**
  31. * 值是对象类型的列表
  32. *
  33. * 使用自定义的RedisTemplate,将值格式化成JSON
  34. */
  35. @Resource(name = "redisTemplate")
  36. private ListOperations<String, User> userListOperations;
  37. /**
  38. * 值是数字类型的列表
  39. *
  40. * 自定义的RedisTemplate可以格式化Integer
  41. */
  42. @Resource(name = "redisTemplate")
  43. private ListOperations<String, Integer> integerListOperations;
  44. /**
  45. * LRANGE 按指定范围查看列表的值
  46. */
  47. @Test
  48. @Order(1)
  49. void range() {
  50. // 插入元素
  51. stringListOperations.rightPushAll("list", "a", "b", "c", "d");
  52. List<String> list = stringListOperations.range("list", 0, -1);
  53. assertNotNull(list);
  54. assertEquals(4, list.size());
  55. assertEquals("a", list.get(0));
  56. redisTemplate.delete("list");
  57. }
  58. /**
  59. * RPUSH 在列表右侧插值
  60. *
  61. * RPUSHX 仅key存在时在列表右侧插值
  62. *
  63. * LPUSH 在列表左侧插值
  64. *
  65. * LPUSHX 仅key存在时在列表左侧插值
  66. */
  67. @Test
  68. @Order(2)
  69. void push() {
  70. // RPUSH 在列表右侧插入单个元素
  71. stringListOperations.rightPush("list1", "a");
  72. assertEquals(1, stringListOperations.size("list1"));
  73. // RPUSH 在列表右侧插入多个元素
  74. stringListOperations.rightPushAll("list1", "b", "c", "d");
  75. // stringListOperations.rightPushAll("list1", Arrays.asList("b", "c", "d"));
  76. assertEquals(4, stringListOperations.size("list1"));
  77. // RPUSHX 仅key存在时在列表右侧插值
  78. stringListOperations.rightPushIfPresent("list1", "e");
  79. assertEquals(5, stringListOperations.size("list1"));
  80. // LPUSH 在列表左侧插入单个元素
  81. stringListOperations.leftPush("list2", "a");
  82. assertEquals(1, stringListOperations.size("list2"));
  83. // LPUSH 在列表左侧插入多个元素
  84. stringListOperations.leftPushAll("list2", "b", "c");
  85. // stringListOperations.leftPushAll("list2", Arrays.asList("d", "e"));
  86. assertEquals(3, stringListOperations.size("list2"));
  87. // LPUSHX 仅key存在时在列表左侧插值
  88. stringListOperations.leftPushIfPresent("list2", "d");
  89. assertEquals(4, stringListOperations.size("list2"));
  90. // LINSERT 插入元素
  91. // 在指定元素左侧插入
  92. stringListOperations.leftPush("list1", "b", "hello");
  93. assertEquals("hello", stringListOperations.index("list1", 1));
  94. // 在指定元素右侧插入
  95. stringListOperations.rightPush("list2", "c", "world");
  96. assertEquals("world", stringListOperations.index("list2", 2));
  97. // 插入的类型需要相同,对应类型使用对应的Operations
  98. // 例如:int类型
  99. integerListOperations.rightPushAll("nums", 1, 2, 3);
  100. assertEquals(3, integerListOperations.size("nums"));
  101. // 例如:User类型
  102. User user = new User(1, "tom", new BigDecimal("165.3"));
  103. userListOperations.rightPush("users", user);
  104. User user2 = userListOperations.rightPop("users");
  105. assertEquals(user, user2);
  106. // 清空数据
  107. redisTemplate.delete(Arrays.asList("list1", "list2", "nums", "users"));
  108. }
  109. /**
  110. * RPOP 从列表末尾获取元素并删除
  111. *
  112. * LPOP 从列表头部获取元素并删除
  113. */
  114. @Test
  115. @Order(3)
  116. void pop() {
  117. stringListOperations.rightPushAll("list", "a", "b", "c", "d");
  118. // RPOP 从列表末尾获取元素并删除
  119. String s1 = stringListOperations.rightPop("list");
  120. assertEquals("d", s1);
  121. // LPOP 从列表头部获取元素并删除
  122. String s2 = stringListOperations.leftPop("list");
  123. assertEquals("a", s2);
  124. // 清空数据
  125. redisTemplate.delete("list");
  126. }
  127. @Test
  128. @Order(4)
  129. void other() {
  130. Long size1 = stringListOperations.rightPushAll("list", "a", "b", "c", "d", "e");
  131. // LLEN 列表长度
  132. Long size2 = stringListOperations.size("list");
  133. assertEquals(size1, size2);
  134. // LINDEX 获取指定索引的值
  135. String first = stringListOperations.index("list", 0);
  136. assertEquals("a", first);
  137. // LSET 修改指定索引的值
  138. stringListOperations.set("list", 2, "world");
  139. assertEquals("world", stringListOperations.index("list", 2));
  140. // LREM 删除指定元素
  141. Long remove = stringListOperations.remove("list", 1, "b");
  142. assertEquals(1, remove);
  143. // LTRIM 裁剪列表
  144. stringListOperations.trim("list", 1, 2);
  145. assertEquals(2, stringListOperations.size("list"));
  146. // RPOPLPUSH 从source列表右边吐出一个值,插到destination列表左边
  147. String move = stringListOperations.rightPopAndLeftPush("list", "list2");
  148. assertEquals("d", move);
  149. assertEquals(1, stringListOperations.size("list"));
  150. assertEquals(1, stringListOperations.size("list2"));
  151. // 清空数据
  152. redisTemplate.delete(Arrays.asList("list", "list2"));
  153. }
  154. }

SetOperations Set 集合

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.junit.jupiter.api.Assertions.assertFalse;
  3. import static org.junit.jupiter.api.Assertions.assertNotNull;
  4. import static org.junit.jupiter.api.Assertions.assertTrue;
  5. import java.math.BigDecimal;
  6. import java.util.Arrays;
  7. import java.util.List;
  8. import java.util.Set;
  9. import javax.annotation.Resource;
  10. import org.junit.jupiter.api.Order;
  11. import org.junit.jupiter.api.Test;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.boot.test.context.SpringBootTest;
  14. import org.springframework.data.redis.core.RedisTemplate;
  15. import org.springframework.data.redis.core.SetOperations;
  16. import com.maxqiu.demo.entity.User;
  17. /**
  18. * Set 集合
  19. */
  20. @SpringBootTest
  21. public class SetOperationsTest {
  22. @Autowired
  23. private RedisTemplate<String, Object> redisTemplate;
  24. /**
  25. * 值是String类型的集合
  26. *
  27. * 自定义的RedisTemplate在格式化String时会多出引号
  28. *
  29. * 需要使用Spring内置的StringRedisTemplate
  30. */
  31. @Resource(name = "stringRedisTemplate")
  32. private SetOperations<String, String> stringSetOperations;
  33. /**
  34. * 值是对象类型的集合
  35. *
  36. * 使用自定义的RedisTemplate,将值格式化成JSON
  37. */
  38. @Resource(name = "redisTemplate")
  39. private SetOperations<String, User> userSetOperations;
  40. /**
  41. * 值是数字类型的集合
  42. *
  43. * 自定义的RedisTemplate可以格式化Integer
  44. */
  45. @Resource(name = "redisTemplate")
  46. private SetOperations<String, Integer> integerSetOperations;
  47. /**
  48. * 测试基本操作
  49. */
  50. @Test
  51. @Order(1)
  52. void normal() {
  53. // SADD 添加元素到集合中
  54. Long add = stringSetOperations.add("set", "a", "b", "c", "d", "e");
  55. assertEquals(5, add);
  56. // SCARD 集合大小
  57. Long size = stringSetOperations.size("set");
  58. assertEquals(add, size);
  59. // SMEMBERS 列出集合中的元素
  60. Set<String> set = stringSetOperations.members("set");
  61. assertNotNull(set);
  62. assertEquals(5, set.size());
  63. // SISMEMBER 存在指定的元素
  64. Boolean member = stringSetOperations.isMember("set", "a");
  65. assertTrue(member);
  66. // SREM 删除指定元素
  67. Long remove = stringSetOperations.remove("set", "a");
  68. assertEquals(1, remove);
  69. // SMOVE 移动指定元素
  70. Boolean move = stringSetOperations.move("set", "b", "set2");
  71. assertTrue(move);
  72. // SRANDMEMBER 随机取出一个或多个元素
  73. // 取出一个元素
  74. String set1 = stringSetOperations.randomMember("set");
  75. System.out.println(set1);
  76. // 取出多个元素且不重复
  77. Set<String> set3 = stringSetOperations.distinctRandomMembers("set", 2);
  78. assertEquals(2, set3.size());
  79. // 取出多个元素但可能有重复
  80. List<String> list = stringSetOperations.randomMembers("set", 2);
  81. assertEquals(2, list.size());
  82. // SPOP 随机取出一个或多个元素并删除
  83. String set2 = stringSetOperations.pop("set");
  84. System.out.println(set2);
  85. List<String> list2 = stringSetOperations.pop("set", 2);
  86. assertNotNull(list2);
  87. assertEquals(2, list2.size());
  88. // 删除数据
  89. assertFalse(redisTemplate.hasKey("set"));
  90. redisTemplate.delete("set2");
  91. }
  92. /**
  93. * 测试不同类型
  94. */
  95. @Test
  96. @Order(2)
  97. void type() {
  98. // 对象类型
  99. Long add = userSetOperations.add("users", new User(1, "tom", new BigDecimal(185)),
  100. new User(2, "tom", new BigDecimal(183)));
  101. Long size = userSetOperations.size("users");
  102. assertEquals(add, size);
  103. // 数字类型
  104. Long nums = integerSetOperations.add("nums", 1, 2, 3, 3);
  105. assertEquals(3, nums);
  106. // 删除数据
  107. redisTemplate.delete(Arrays.asList("users", "nums"));
  108. }
  109. /**
  110. * SINTER 交集
  111. *
  112. * SUNION 并集
  113. *
  114. * SDIFF 差集
  115. */
  116. @Test
  117. @Order(3)
  118. void aggregate() {
  119. // 准备数据
  120. stringSetOperations.add("set1", "a", "b", "c");
  121. stringSetOperations.add("set2", "a", "d", "f");
  122. // SINTER 交集
  123. Set<String> intersect = stringSetOperations.intersect("set1", "set2");
  124. // stringSetOperations.intersect(Arrays.asList("set1", "set2"));
  125. // stringSetOperations.intersect("set1", Arrays.asList("set2"));
  126. assertEquals(1, intersect.size());
  127. assertTrue(intersect.contains("a"));
  128. // SINTERSTORE 计算交集并存入目标集合
  129. Long intersectAndStore = stringSetOperations.intersectAndStore("set1", "set2", "set3");
  130. assertEquals(1, intersectAndStore);
  131. // SUNION 并集
  132. Set<String> union = stringSetOperations.union("set1", "set2");
  133. assertEquals(5, union.size());
  134. // SUNIONSTORE 计算并集并存入目标集合
  135. Long unionAndStore = stringSetOperations.unionAndStore("set1", "set2", "set4");
  136. assertEquals(5, unionAndStore);
  137. // SDIFF 差集
  138. Set<String> difference = stringSetOperations.difference("set1", "set2");
  139. assertEquals(2, difference.size());
  140. assertTrue(difference.contains("b"));
  141. // SDIFFSTORE 计算差集并存入目标集合
  142. Long differenceAndStore = stringSetOperations.differenceAndStore("set1", "set2", "set5");
  143. assertEquals(2, differenceAndStore);
  144. // 删除数据
  145. redisTemplate.delete(Arrays.asList("set1", "set2", "set3", "set4", "set5"));
  146. }
  147. }

HashOperations Hash 哈希散列

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.junit.jupiter.api.Assertions.assertFalse;
  3. import static org.junit.jupiter.api.Assertions.assertTrue;
  4. import java.util.Arrays;
  5. import java.util.List;
  6. import java.util.Map;
  7. import java.util.Set;
  8. import javax.annotation.Resource;
  9. import org.junit.jupiter.api.Order;
  10. import org.junit.jupiter.api.Test;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.boot.test.context.SpringBootTest;
  13. import org.springframework.data.redis.core.HashOperations;
  14. import org.springframework.data.redis.core.RedisTemplate;
  15. /**
  16. * Hash 哈希散列
  17. */
  18. @SpringBootTest
  19. public class HashOperationsTest {
  20. @Autowired
  21. private RedisTemplate<String, Object> redisTemplate;
  22. @Resource(name = "stringRedisTemplate")
  23. private HashOperations<String, String, String> stringHashOperations;
  24. @Resource(name = "redisTemplate")
  25. private HashOperations<String, String, Integer> integerHashOperations;
  26. /**
  27. * HSET 给哈希散列添加字段和值
  28. *
  29. * HSETNX 给哈希散列添加字段和值(仅字段不存在)
  30. */
  31. @Test
  32. @Order(1)
  33. void set() {
  34. // HSET 给哈希散列添加字段和值
  35. // 单个
  36. stringHashOperations.put("user", "id", "1");
  37. stringHashOperations.put("user", "name", "tom");
  38. // 多个
  39. stringHashOperations.putAll("user", Map.of("email", "[email protected]", "age", "18"));
  40. // HSETNX 给哈希散列添加字段和值(仅字段不存在)
  41. Boolean flag1 = stringHashOperations.putIfAbsent("user", "address", "jiangsu");
  42. assertTrue(flag1);
  43. Boolean flag2 = stringHashOperations.putIfAbsent("user", "address", "jiangsu");
  44. assertFalse(flag2);
  45. // 删除数据
  46. redisTemplate.delete("user");
  47. }
  48. /**
  49. * HGET 获取哈希散列的指定字段的值
  50. *
  51. * HMGET 获取哈希散列的多个字段的值
  52. *
  53. * HGETALL 获取哈希散列的所有字段与值
  54. *
  55. * HKEYS 获取哈希散列的所有字段
  56. *
  57. * HVALS 获取哈希散列的所有值
  58. */
  59. @Test
  60. @Order(2)
  61. void get() {
  62. // 准备数据
  63. stringHashOperations.putAll("user", Map.of("id", "1", "name", "tom", "email", "[email protected]", "age", "18"));
  64. // HGET 获取哈希散列的指定字段的值
  65. String name = stringHashOperations.get("user", "name");
  66. assertEquals("tom", name);
  67. // HMGET 获取哈希散列的多个字段的值
  68. List<String> list = stringHashOperations.multiGet("user", Arrays.asList("name", "age", "email", "address"));
  69. for (String s : list) {
  70. System.out.println(s);
  71. }
  72. // HGETALL 获取哈希散列的所有字段与值
  73. Map<String, String> map = stringHashOperations.entries("user");
  74. for (String s : map.keySet()) {
  75. System.out.println(s + " " + map.get(s));
  76. }
  77. // HKEYS 获取哈希散列的所有字段
  78. Set<String> keys = stringHashOperations.keys("user");
  79. for (String key : keys) {
  80. System.out.println(key);
  81. }
  82. // HVALS 获取哈希散列的所有值
  83. List<String> values = stringHashOperations.values("user");
  84. for (String value : values) {
  85. System.out.println(value);
  86. }
  87. // 删除数据
  88. redisTemplate.delete("user");
  89. }
  90. @Test
  91. @Order(3)
  92. void other() {
  93. // 准备数据
  94. stringHashOperations.putAll("user", Map.of("id", "1", "name", "tom", "email", "[email protected]", "age", "18"));
  95. // HEXISTS 哈希散列是否存在指定字段
  96. Boolean hasName = stringHashOperations.hasKey("user", "name");
  97. assertTrue(hasName);
  98. // HDEL 删除哈希散列的指定字段
  99. Long delete = stringHashOperations.delete("user", "id", "email");
  100. assertEquals(2, delete);
  101. // HLEN 哈希散列的长度
  102. Long size = stringHashOperations.size("user");
  103. assertEquals(2, size);
  104. // HSTRLEN 哈希散列的字段的长度
  105. Long length = stringHashOperations.lengthOfValue("user", "name");
  106. assertEquals(3, length);
  107. // HINCRBY 增减哈希散列中指定字段的值
  108. Long increment = integerHashOperations.increment("user", "age", 1);
  109. assertEquals(19, increment);
  110. // 删除数据
  111. redisTemplate.delete("user");
  112. }
  113. }

ZSetOperations Zset 有序集合(Sorted Sets)

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.junit.jupiter.api.Assertions.assertTrue;
  3. import java.util.HashSet;
  4. import java.util.Set;
  5. import javax.annotation.Resource;
  6. import org.junit.jupiter.api.Order;
  7. import org.junit.jupiter.api.Test;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.boot.test.context.SpringBootTest;
  10. import org.springframework.data.redis.core.DefaultTypedTuple;
  11. import org.springframework.data.redis.core.RedisOperations;
  12. import org.springframework.data.redis.core.ZSetOperations;
  13. /**
  14. * ZSet 有序集合(Sorted Sets)
  15. */
  16. @SpringBootTest
  17. public class ZSetOperationsTest {
  18. @Autowired
  19. private RedisOperations<String, Object> redisOperations;
  20. /**
  21. * 值是String类型的有序集合
  22. *
  23. * 自定义的RedisTemplate在格式化String时会多出引号
  24. *
  25. * 需要使用Spring内置的StringRedisTemplate
  26. */
  27. @Resource(name = "stringRedisTemplate")
  28. private ZSetOperations<String, String> stringZSetOperations;
  29. /**
  30. * ZADD 将一个或多个member及source添加到有序集合中
  31. */
  32. @Test
  33. @Order(1)
  34. void add() {
  35. Boolean flag = stringZSetOperations.add("zset", "maths", 99);
  36. assertTrue(flag);
  37. Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
  38. set.add(new DefaultTypedTuple<>("chinese", 90.0));
  39. set.add(new DefaultTypedTuple<>("english", 60.0));
  40. Long add = stringZSetOperations.add("zset", set);
  41. assertEquals(2, add);
  42. // 删除数据
  43. redisOperations.delete("zset");
  44. }
  45. /**
  46. * 根据指定规则排序取出
  47. */
  48. @Test
  49. @Order(2)
  50. void get() {
  51. // 准备数据
  52. Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
  53. set.add(new DefaultTypedTuple<>("chinese", 115.0));
  54. set.add(new DefaultTypedTuple<>("maths", 135.5));
  55. set.add(new DefaultTypedTuple<>("english", 110.5));
  56. set.add(new DefaultTypedTuple<>("physics", 88.0));
  57. set.add(new DefaultTypedTuple<>("chemistry", 65.0));
  58. stringZSetOperations.add("zset", set);
  59. /// 正序
  60. // ZRANGE 返回排序集合中的指定范围的元素(正序,从小到大)
  61. Set<String> zset = stringZSetOperations.range("zset", 0, -1);
  62. System.out.println(zset);
  63. // ZRANGEBYSCORE 返回指定分数内的元素(正序,从小到大)
  64. Set<String> zset3 = stringZSetOperations.rangeByScore("zset", 10, 100);
  65. System.out.println(zset3);
  66. // ZRANGEBYSCORE 返回指定分数内的元素(正序,从小到大)(LIMIT 分页)
  67. Set<String> zset5 = stringZSetOperations.rangeByScore("zset", 0, 200, 1, 2);
  68. System.out.println(zset5);
  69. // ZRANGE xxxWithScores 返回时包含分数(正序,从小到大)其他同
  70. Set<ZSetOperations.TypedTuple<String>> zset2 = stringZSetOperations.rangeWithScores("zset", 0, -1);
  71. if (zset2 != null) {
  72. for (ZSetOperations.TypedTuple<String> stringTypedTuple : zset2) {
  73. System.out.println(stringTypedTuple.getValue() + " " + stringTypedTuple.getScore());
  74. }
  75. }
  76. /// 倒序
  77. // ZREVRANGE 返回排序集合中的指定范围的元素(倒序,从大到小)
  78. stringZSetOperations.reverseRange("zset", 0, -1);
  79. // ZREVRANGEBYSCORE 返回指定分数内的元素(倒序,从大到小)
  80. stringZSetOperations.reverseRangeByScore("zset", 10, 100);
  81. // ZREVRANGEBYSCORE 返回指定分数内的元素(倒序,从大到小)(LIMIT 分页)
  82. stringZSetOperations.reverseRangeByScore("zset", 0, 200, 1, 2);
  83. // ZREVRANGE xxxWithScores 返回时包含分数(倒序,从大到小)其他同
  84. stringZSetOperations.reverseRangeWithScores("zset", 0, -1);
  85. // 删除数据
  86. redisOperations.delete("zset");
  87. }
  88. @Test
  89. @Order(3)
  90. void other() {
  91. // 准备数据
  92. Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
  93. set.add(new DefaultTypedTuple<>("chinese", 115.0));
  94. set.add(new DefaultTypedTuple<>("maths", 135.5));
  95. set.add(new DefaultTypedTuple<>("english", 110.5));
  96. set.add(new DefaultTypedTuple<>("physics", 88.0));
  97. set.add(new DefaultTypedTuple<>("chemistry", 65.0));
  98. stringZSetOperations.add("zset", set);
  99. // ZSCORE 返回排序集中元素的分数
  100. Double score = stringZSetOperations.score("zset", "chinese");
  101. assertEquals(115.0, score);
  102. // 查询元素在有序集合中的排名
  103. // ZRANK
  104. Long rank = stringZSetOperations.rank("zset", "chinese");
  105. assertEquals(3, rank);
  106. // ZREVRANK
  107. Long reverseRank = stringZSetOperations.reverseRank("zset", "chinese");
  108. assertEquals(1, reverseRank);
  109. // ZCARD key 返回排序集合的元素数量
  110. Long size = stringZSetOperations.size("zset");
  111. assertEquals(5, size);
  112. // ZCOUNT 统计指定范围内的元素数量
  113. Long count = stringZSetOperations.count("zset", 0, 100);
  114. assertEquals(2, count);
  115. // ZINCRBY 给指定元素增减分数
  116. Double incrementScore = stringZSetOperations.incrementScore("zset", "english", 18.5);
  117. assertEquals(129.0, incrementScore);
  118. // ZREM 删除元素
  119. Long remove = stringZSetOperations.remove("zset", "chinese", "english");
  120. assertEquals(2, remove);
  121. // 删除数据
  122. redisOperations.delete("zset");
  123. }
  124. }

Transactions 事务

  1. import java.util.Arrays;
  2. import java.util.List;
  3. import javax.annotation.Resource;
  4. import org.junit.jupiter.api.Assertions;
  5. import org.junit.jupiter.api.Test;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.boot.test.context.SpringBootTest;
  8. import org.springframework.dao.DataAccessException;
  9. import org.springframework.data.redis.core.RedisOperations;
  10. import org.springframework.data.redis.core.RedisTemplate;
  11. import org.springframework.data.redis.core.SessionCallback;
  12. import org.springframework.data.redis.core.ValueOperations;
  13. /**
  14. * 事务
  15. */
  16. @SpringBootTest
  17. public class TransactionsTest {
  18. @Autowired
  19. private RedisTemplate<String, Object> redisTemplate;
  20. /**
  21. * 值是数字类型的字符串
  22. *
  23. * 自定义的RedisTemplate可以格式化Integer
  24. */
  25. @Resource(name = "redisTemplate")
  26. private ValueOperations<String, Integer> valueOperations;
  27. @Test
  28. void test() {
  29. valueOperations.set("a", 100);
  30. valueOperations.set("b", 100);
  31. // 乐观锁
  32. // redisOperations.watch("a");
  33. List<Long> execute = redisTemplate.execute(new SessionCallback<>() {
  34. @Override
  35. public List<Long> execute(RedisOperations operations) throws DataAccessException {
  36. operations.multi();
  37. ValueOperations<String, Long> valueOperations = operations.opsForValue();
  38. valueOperations.increment("a", 10);
  39. valueOperations.decrement("b", 10);
  40. return operations.exec();
  41. }
  42. });
  43. Assertions.assertEquals(110, execute.get(0));
  44. Assertions.assertEquals(90, execute.get(1));
  45. // 删除数据
  46. redisTemplate.delete(Arrays.asList("a", "b"));
  47. }
  48. }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK