SpringBoot2.4.x整合Redis(RedisTemplate操作五大常用数据类型)
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.
示例代码:
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
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Jackson格式化 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- 连接池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
yml配置文件
根据Redis服务的模式不同,对应的配置文件也不同
spring:
redis:
## 单机模式
host: 192.168.220.101 # 地址
port: 6379 # 端口
# 通用配置
username: # 用户名
password: 123 # 密码
database: 0 # 指定数据库序号
ssl: false # 是否启用SSL
connect-timeout: 1000 # 连接超时时间(毫秒)
timeout: 1000 # 操作超时时间(毫秒)
client-name: # 客户端名称(不知道干嘛用的)
client-type: lettuce # 驱动类型
# 连接池配置
lettuce:
pool:
min-idle: 1 # 最小空闲连接(默认0)
max-idle: 8 # 最大空闲连接(默认8)
max-active: 16 # 最大连接数(默认8,使用负值表示没有限制)
max-wait: -1ms # 最大阻塞等待时间(默认-1,负数表示没限制)
哨兵模式(主从复制)
spring:
redis:
## 哨兵模式(主从复制)
sentinel:
master: master # 哨兵的sentinel.conf配置文件中的主节点名称
password: 123 # 哨兵的密码
nodes: 192.168.220.101:26379,192.168.220.102:26379,192.168.220.103:26379 # 哨兵节点
# 通用配置
username: # 用户名
password: 123 # 密码
database: 0 # 指定数据库序号
ssl: false # 是否启用SSL
connect-timeout: 1000 # 连接超时时间(毫秒)
timeout: 1000 # 操作超时时间(毫秒)
client-name: # 客户端名称(不知道干嘛用的)
client-type: lettuce # 驱动类型
# 连接池配置
lettuce:
pool:
min-idle: 1 # 最小空闲连接(默认0)
max-idle: 8 # 最大空闲连接(默认8)
max-active: 16 # 最大连接数(默认8,使用负值表示没有限制)
max-wait: -1ms # 最大阻塞等待时间(默认-1,负数表示没限制)
spring:
redis:
## 集群模式
cluster:
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
# 通用配置
username: # 用户名
password: 123 # 密码
database: 0 # 指定数据库序号
ssl: false # 是否启用SSL
connect-timeout: 1000 # 连接超时时间(毫秒)
timeout: 1000 # 操作超时时间(毫秒)
client-name: # 客户端名称(不知道干嘛用的)
client-type: lettuce # 驱动类型
# 连接池配置
lettuce:
pool:
min-idle: 1 # 最小空闲连接(默认0)
max-idle: 8 # 最大空闲连接(默认8)
max-active: 16 # 最大连接数(默认8,使用负值表示没有限制)
max-wait: -1ms # 最大阻塞等待时间(默认-1,负数表示没限制)
cluster:
refresh:
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
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
/**
* Redis配置项
*/
@Configuration
public class RedisConfig {
/**
* 自定义RedisTemplate,使用json格式化value
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 连接工厂
template.setConnectionFactory(redisConnectionFactory);
// key序列化
template.setKeySerializer(RedisSerializer.string());
// value序列化
template.setValueSerializer(RedisSerializer.json());
// hash序列化
template.setHashKeySerializer(RedisSerializer.string());
template.setHashValueSerializer(RedisSerializer.json());
// 启用事务支持
template.setEnableTransactionSupport(true);
return template;
}
}
一般情况下,有如下两个常用对象
/**
* RedisTemplate操作通用对象
*/
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 操作String类型的对象
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
而RedisTemplate
又可以通过opsForXxx
生成对应的类型的操作对象,例如
// RedisTemplate 可以获取对应数据类型的 XxxOperations
ValueOperations<String, Object> objectValueOperations = redisTemplate.opsForValue();
ListOperations<String, Object> objectListOperations = redisTemplate.opsForList();
SetOperations<String, Object> objectSetOperations = redisTemplate.opsForSet();
HashOperations<String, String, Object> objectHashOperations = redisTemplate.opsForHash();
ZSetOperations<String, Object> objectZSetOperations = redisTemplate.opsForZSet();
简单点,若后面要获取XxxOperations
可以直接使用@Resource
注解获取,且获取时可以将Object
对象根据值类型换成具体的对象。注意,值是String
类型时name
需要使用stringRedisTemplate
/**
* 值是String类型的字符串
*
* 自定义的RedisTemplate在格式化String时会多出引号
*
* 需要使用Spring内置的StringRedisTemplate
*/
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> stringValueOperations;
/**
* 值是对象类型的字符串
*
* 使用自定义的RedisTemplate,将值格式化成JSON
*/
@Resource(name = "redisTemplate")
private ValueOperations<String, User> userValueOperations;
/**
* 值是数字类型的字符串
*
* 自定义的RedisTemplate可以格式化Integer
*/
@Resource(name = "redisTemplate")
private ValueOperations<String, Integer> integerValueOperations;
RedisTemplate
基础操作
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
/**
* Keys 键相关
*/
@SpringBootTest
public class BaseOperationsTest {
/**
* RedisTemplate操作通用对象
*/
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 操作String类型的对象
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void test() {
// RedisTemplate 可以获取对应数据类型的 XxxOperations
ValueOperations<String, Object> objectValueOperations = redisTemplate.opsForValue();
ListOperations<String, Object> objectListOperations = redisTemplate.opsForList();
SetOperations<String, Object> objectSetOperations = redisTemplate.opsForSet();
HashOperations<String, String, Object> objectHashOperations = redisTemplate.opsForHash();
ZSetOperations<String, Object> objectZSetOperations = redisTemplate.opsForZSet();
// 准备数据
ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
stringValueOperations.set("key1", "hello1");
stringValueOperations.set("key2", "hello2");
stringValueOperations.set("key3", "hello3");
stringValueOperations.set("key4", "hello4");
// KEYS 查找键
Set<String> keys = redisTemplate.keys("*");
assertNotNull(keys);
assertEquals(4, keys.size());
// EXISTS 键是否存在
Boolean hasKey = redisTemplate.hasKey("key1");
assertTrue(hasKey);
Long existingKeys = redisTemplate.countExistingKeys(Arrays.asList("key1", "key2"));
assertEquals(2, existingKeys);
// TYPE 查看键的类型
assertEquals(DataType.STRING, redisTemplate.type("key1"));
// DEL 删除键
Boolean deleteFalse = redisTemplate.delete("key");
assertFalse(deleteFalse);
Boolean deleteSuccess = redisTemplate.delete("key1");
assertTrue(deleteSuccess);
Long batchDelete = redisTemplate.delete(Arrays.asList("key", "key2"));
assertEquals(1, batchDelete);
// UNLINK 删除键(异步)
Boolean unlink = redisTemplate.unlink("key3");
assertTrue(unlink);
Long batchUnlink = redisTemplate.unlink(Arrays.asList("key1", "key2"));
assertEquals(0, batchUnlink);
Long expire = redisTemplate.getExpire("key4");
// TTL 查看键剩余生成秒
assertEquals(-1, expire);
// EXPIRE 设置键到期秒
redisTemplate.expire("key4", 10, TimeUnit.SECONDS);
redisTemplate.expire("key4", Duration.ofSeconds(10));
assertEquals(10, redisTemplate.getExpire("key4"));
// PERSIST key 设置键永不过期
redisTemplate.persist("key4");
assertEquals(-1, redisTemplate.getExpire("key4"));
// 删除数据
assertTrue(redisTemplate.delete("key4"));
}
}
ValueOperations
String 字符串
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import com.maxqiu.demo.entity.User;
/**
* String 字符串
*/
@SpringBootTest
public class StringOperationsTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 值是String类型的字符串
*
* 自定义的RedisTemplate在格式化String时会多出引号
*
* 需要使用Spring内置的StringRedisTemplate
*/
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> stringValueOperations;
/**
* 值是对象类型的字符串
*
* 使用自定义的RedisTemplate,将值格式化成JSON
*/
@Resource(name = "redisTemplate")
private ValueOperations<String, User> userValueOperations;
/**
* 值是数字类型的字符串
*
* 自定义的RedisTemplate可以格式化Integer
*/
@Resource(name = "redisTemplate")
private ValueOperations<String, Integer> integerValueOperations;
/**
* GET
*/
@Test
@Order(1)
void get() {
// 获取不存在的键
assertNull(stringValueOperations.get("nonexistent"));
// 获取String类型的数据
stringValueOperations.set("key", "hello");
assertEquals("hello", stringValueOperations.get("key"));
// 获取对象类型的数据
User user = new User(1, "tom", new BigDecimal(18));
userValueOperations.set("user", user);
assertEquals(user, userValueOperations.get("user"));
integerValueOperations.set("num", 1);
assertEquals(1, integerValueOperations.get("num"));
redisTemplate.delete(Arrays.asList("user", "key", "num"));
}
/**
* SET
*
* SETEX
*
* SETNX
*
* GETSET
*/
@Test
@Order(2)
void set() {
/// SET 设置键与值
// 简单设置值
stringValueOperations.set("key1", "hello");
assertEquals("hello", stringValueOperations.get("key1"));
// 如果键存在才设置值
Boolean flag1 = stringValueOperations.setIfPresent("key1", "world");
assertTrue(flag1);
assertEquals("world", stringValueOperations.get("key1"));
Boolean flag2 = stringValueOperations.setIfPresent("key2", "world");
assertFalse(flag2);
assertNull(stringValueOperations.get("key2"));
// 同上,并设置超时时间
Boolean flag3 = stringValueOperations.setIfPresent("key1", "hello", Duration.ofSeconds(10));
assertTrue(flag3);
// stringOperations.setIfPresent("key2", "world", 10, TimeUnit.SECONDS);
assertEquals(10, redisTemplate.getExpire("key1"));
// SETEX 设置键与值并设置超时时间
stringValueOperations.set("key2", "world", Duration.ofSeconds(10));
// stringOperations.set("key2", "world", 10, TimeUnit.SECONDS);
assertEquals(10, redisTemplate.getExpire("key2"));
// SETNX 键不存在则设置键与值
Boolean flag4 = stringValueOperations.setIfAbsent("key3", "hello");
assertTrue(flag4);
assertEquals("hello", stringValueOperations.get("key3"));
// SET ... NX 键不存在则设置键与值(同时设置时间)
Boolean flag5 = stringValueOperations.setIfAbsent("key4", "world", Duration.ofSeconds(10));
// stringOperations.setIfAbsent("key4", "world", 10, TimeUnit.SECONDS);
assertTrue(flag5);
assertEquals("world", stringValueOperations.get("key4"));
// GETSET 获取值并设置一个新值
String s = stringValueOperations.getAndSet("key4", "redis");
assertEquals(s, "world");
assertEquals("redis", stringValueOperations.get("key4"));
assertEquals(4, redisTemplate.delete(Arrays.asList("key1", "key2", "key3", "key4")));
}
@Test
@Order(3)
void other() {
// APPEND 拼接字符串
Integer append1 = stringValueOperations.append("key", "hello");
assertEquals(5, append1);
Integer append2 = stringValueOperations.append("key", " world");
assertEquals(11, append2);
// STRLEN 获取字符串长度
Long size = stringValueOperations.size("key");
assertEquals(11, size);
// GETRANGE 截取字符串
String a1 = stringValueOperations.get("key", 0, 3);
assertEquals("hell", a1);
String a2 = stringValueOperations.get("key", -3, -1);
assertEquals("rld", a2);
String a3 = stringValueOperations.get("key", 0, -1);
assertEquals("hello world", a3);
String a4 = stringValueOperations.get("key", 20, 100);
assertEquals("", a4);
// SETRANGE 修改字符串
stringValueOperations.set("key", "redis", 6);
assertEquals("hello redis", stringValueOperations.get("key"));
// MSET 批量设置值
stringValueOperations.multiSet(Map.of("key1", "v1", "key2", "v2"));
assertTrue(redisTemplate.hasKey("key1"));
// MGET 批量获取值
List<String> list = stringValueOperations.multiGet(Arrays.asList("key1", "key2"));
assertNotNull(list);
assertEquals(2, list.size());
// MSETNX 批量设置值(仅当键不存在)
Boolean flag = stringValueOperations.multiSetIfAbsent(Map.of("key1", "v1", "key3", "v3"));
assertFalse(flag);
integerValueOperations.set("num", 1);
// INCR 加一
Long num1 = integerValueOperations.increment("num");
assertEquals(2, num1);
// DECR 减一
Long num2 = integerValueOperations.decrement("num");
assertEquals(1, num2);
// INCRBY 加N
Long num3 = integerValueOperations.increment("num", 15);
assertEquals(16, num3);
// DECRBY 减N
Long num4 = integerValueOperations.decrement("num", 6);
assertEquals(10, num4);
redisTemplate.delete(Arrays.asList("key", "key1", "key2", "num"));
}
}
ListOperations
List 列表
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import com.maxqiu.demo.entity.User;
/**
* List 列表
*/
@SpringBootTest
public class ListOperationsTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 值是String类型的列表
*
* 自定义的RedisTemplate在格式化String时会多出引号
*
* 需要使用Spring内置的StringRedisTemplate
*/
@Resource(name = "stringRedisTemplate")
private ListOperations<String, String> stringListOperations;
/**
* 值是对象类型的列表
*
* 使用自定义的RedisTemplate,将值格式化成JSON
*/
@Resource(name = "redisTemplate")
private ListOperations<String, User> userListOperations;
/**
* 值是数字类型的列表
*
* 自定义的RedisTemplate可以格式化Integer
*/
@Resource(name = "redisTemplate")
private ListOperations<String, Integer> integerListOperations;
/**
* LRANGE 按指定范围查看列表的值
*/
@Test
@Order(1)
void range() {
// 插入元素
stringListOperations.rightPushAll("list", "a", "b", "c", "d");
List<String> list = stringListOperations.range("list", 0, -1);
assertNotNull(list);
assertEquals(4, list.size());
assertEquals("a", list.get(0));
redisTemplate.delete("list");
}
/**
* RPUSH 在列表右侧插值
*
* RPUSHX 仅key存在时在列表右侧插值
*
* LPUSH 在列表左侧插值
*
* LPUSHX 仅key存在时在列表左侧插值
*/
@Test
@Order(2)
void push() {
// RPUSH 在列表右侧插入单个元素
stringListOperations.rightPush("list1", "a");
assertEquals(1, stringListOperations.size("list1"));
// RPUSH 在列表右侧插入多个元素
stringListOperations.rightPushAll("list1", "b", "c", "d");
// stringListOperations.rightPushAll("list1", Arrays.asList("b", "c", "d"));
assertEquals(4, stringListOperations.size("list1"));
// RPUSHX 仅key存在时在列表右侧插值
stringListOperations.rightPushIfPresent("list1", "e");
assertEquals(5, stringListOperations.size("list1"));
// LPUSH 在列表左侧插入单个元素
stringListOperations.leftPush("list2", "a");
assertEquals(1, stringListOperations.size("list2"));
// LPUSH 在列表左侧插入多个元素
stringListOperations.leftPushAll("list2", "b", "c");
// stringListOperations.leftPushAll("list2", Arrays.asList("d", "e"));
assertEquals(3, stringListOperations.size("list2"));
// LPUSHX 仅key存在时在列表左侧插值
stringListOperations.leftPushIfPresent("list2", "d");
assertEquals(4, stringListOperations.size("list2"));
// LINSERT 插入元素
// 在指定元素左侧插入
stringListOperations.leftPush("list1", "b", "hello");
assertEquals("hello", stringListOperations.index("list1", 1));
// 在指定元素右侧插入
stringListOperations.rightPush("list2", "c", "world");
assertEquals("world", stringListOperations.index("list2", 2));
// 插入的类型需要相同,对应类型使用对应的Operations
// 例如:int类型
integerListOperations.rightPushAll("nums", 1, 2, 3);
assertEquals(3, integerListOperations.size("nums"));
// 例如:User类型
User user = new User(1, "tom", new BigDecimal("165.3"));
userListOperations.rightPush("users", user);
User user2 = userListOperations.rightPop("users");
assertEquals(user, user2);
// 清空数据
redisTemplate.delete(Arrays.asList("list1", "list2", "nums", "users"));
}
/**
* RPOP 从列表末尾获取元素并删除
*
* LPOP 从列表头部获取元素并删除
*/
@Test
@Order(3)
void pop() {
stringListOperations.rightPushAll("list", "a", "b", "c", "d");
// RPOP 从列表末尾获取元素并删除
String s1 = stringListOperations.rightPop("list");
assertEquals("d", s1);
// LPOP 从列表头部获取元素并删除
String s2 = stringListOperations.leftPop("list");
assertEquals("a", s2);
// 清空数据
redisTemplate.delete("list");
}
@Test
@Order(4)
void other() {
Long size1 = stringListOperations.rightPushAll("list", "a", "b", "c", "d", "e");
// LLEN 列表长度
Long size2 = stringListOperations.size("list");
assertEquals(size1, size2);
// LINDEX 获取指定索引的值
String first = stringListOperations.index("list", 0);
assertEquals("a", first);
// LSET 修改指定索引的值
stringListOperations.set("list", 2, "world");
assertEquals("world", stringListOperations.index("list", 2));
// LREM 删除指定元素
Long remove = stringListOperations.remove("list", 1, "b");
assertEquals(1, remove);
// LTRIM 裁剪列表
stringListOperations.trim("list", 1, 2);
assertEquals(2, stringListOperations.size("list"));
// RPOPLPUSH 从source列表右边吐出一个值,插到destination列表左边
String move = stringListOperations.rightPopAndLeftPush("list", "list2");
assertEquals("d", move);
assertEquals(1, stringListOperations.size("list"));
assertEquals(1, stringListOperations.size("list2"));
// 清空数据
redisTemplate.delete(Arrays.asList("list", "list2"));
}
}
SetOperations
Set 集合
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import com.maxqiu.demo.entity.User;
/**
* Set 集合
*/
@SpringBootTest
public class SetOperationsTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 值是String类型的集合
*
* 自定义的RedisTemplate在格式化String时会多出引号
*
* 需要使用Spring内置的StringRedisTemplate
*/
@Resource(name = "stringRedisTemplate")
private SetOperations<String, String> stringSetOperations;
/**
* 值是对象类型的集合
*
* 使用自定义的RedisTemplate,将值格式化成JSON
*/
@Resource(name = "redisTemplate")
private SetOperations<String, User> userSetOperations;
/**
* 值是数字类型的集合
*
* 自定义的RedisTemplate可以格式化Integer
*/
@Resource(name = "redisTemplate")
private SetOperations<String, Integer> integerSetOperations;
/**
* 测试基本操作
*/
@Test
@Order(1)
void normal() {
// SADD 添加元素到集合中
Long add = stringSetOperations.add("set", "a", "b", "c", "d", "e");
assertEquals(5, add);
// SCARD 集合大小
Long size = stringSetOperations.size("set");
assertEquals(add, size);
// SMEMBERS 列出集合中的元素
Set<String> set = stringSetOperations.members("set");
assertNotNull(set);
assertEquals(5, set.size());
// SISMEMBER 存在指定的元素
Boolean member = stringSetOperations.isMember("set", "a");
assertTrue(member);
// SREM 删除指定元素
Long remove = stringSetOperations.remove("set", "a");
assertEquals(1, remove);
// SMOVE 移动指定元素
Boolean move = stringSetOperations.move("set", "b", "set2");
assertTrue(move);
// SRANDMEMBER 随机取出一个或多个元素
// 取出一个元素
String set1 = stringSetOperations.randomMember("set");
System.out.println(set1);
// 取出多个元素且不重复
Set<String> set3 = stringSetOperations.distinctRandomMembers("set", 2);
assertEquals(2, set3.size());
// 取出多个元素但可能有重复
List<String> list = stringSetOperations.randomMembers("set", 2);
assertEquals(2, list.size());
// SPOP 随机取出一个或多个元素并删除
String set2 = stringSetOperations.pop("set");
System.out.println(set2);
List<String> list2 = stringSetOperations.pop("set", 2);
assertNotNull(list2);
assertEquals(2, list2.size());
// 删除数据
assertFalse(redisTemplate.hasKey("set"));
redisTemplate.delete("set2");
}
/**
* 测试不同类型
*/
@Test
@Order(2)
void type() {
// 对象类型
Long add = userSetOperations.add("users", new User(1, "tom", new BigDecimal(185)),
new User(2, "tom", new BigDecimal(183)));
Long size = userSetOperations.size("users");
assertEquals(add, size);
// 数字类型
Long nums = integerSetOperations.add("nums", 1, 2, 3, 3);
assertEquals(3, nums);
// 删除数据
redisTemplate.delete(Arrays.asList("users", "nums"));
}
/**
* SINTER 交集
*
* SUNION 并集
*
* SDIFF 差集
*/
@Test
@Order(3)
void aggregate() {
// 准备数据
stringSetOperations.add("set1", "a", "b", "c");
stringSetOperations.add("set2", "a", "d", "f");
// SINTER 交集
Set<String> intersect = stringSetOperations.intersect("set1", "set2");
// stringSetOperations.intersect(Arrays.asList("set1", "set2"));
// stringSetOperations.intersect("set1", Arrays.asList("set2"));
assertEquals(1, intersect.size());
assertTrue(intersect.contains("a"));
// SINTERSTORE 计算交集并存入目标集合
Long intersectAndStore = stringSetOperations.intersectAndStore("set1", "set2", "set3");
assertEquals(1, intersectAndStore);
// SUNION 并集
Set<String> union = stringSetOperations.union("set1", "set2");
assertEquals(5, union.size());
// SUNIONSTORE 计算并集并存入目标集合
Long unionAndStore = stringSetOperations.unionAndStore("set1", "set2", "set4");
assertEquals(5, unionAndStore);
// SDIFF 差集
Set<String> difference = stringSetOperations.difference("set1", "set2");
assertEquals(2, difference.size());
assertTrue(difference.contains("b"));
// SDIFFSTORE 计算差集并存入目标集合
Long differenceAndStore = stringSetOperations.differenceAndStore("set1", "set2", "set5");
assertEquals(2, differenceAndStore);
// 删除数据
redisTemplate.delete(Arrays.asList("set1", "set2", "set3", "set4", "set5"));
}
}
HashOperations
Hash 哈希散列
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Hash 哈希散列
*/
@SpringBootTest
public class HashOperationsTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Resource(name = "stringRedisTemplate")
private HashOperations<String, String, String> stringHashOperations;
@Resource(name = "redisTemplate")
private HashOperations<String, String, Integer> integerHashOperations;
/**
* HSET 给哈希散列添加字段和值
*
* HSETNX 给哈希散列添加字段和值(仅字段不存在)
*/
@Test
@Order(1)
void set() {
// HSET 给哈希散列添加字段和值
// 单个
stringHashOperations.put("user", "id", "1");
stringHashOperations.put("user", "name", "tom");
// 多个
stringHashOperations.putAll("user", Map.of("email", "[email protected]", "age", "18"));
// HSETNX 给哈希散列添加字段和值(仅字段不存在)
Boolean flag1 = stringHashOperations.putIfAbsent("user", "address", "jiangsu");
assertTrue(flag1);
Boolean flag2 = stringHashOperations.putIfAbsent("user", "address", "jiangsu");
assertFalse(flag2);
// 删除数据
redisTemplate.delete("user");
}
/**
* HGET 获取哈希散列的指定字段的值
*
* HMGET 获取哈希散列的多个字段的值
*
* HGETALL 获取哈希散列的所有字段与值
*
* HKEYS 获取哈希散列的所有字段
*
* HVALS 获取哈希散列的所有值
*/
@Test
@Order(2)
void get() {
// 准备数据
stringHashOperations.putAll("user", Map.of("id", "1", "name", "tom", "email", "[email protected]", "age", "18"));
// HGET 获取哈希散列的指定字段的值
String name = stringHashOperations.get("user", "name");
assertEquals("tom", name);
// HMGET 获取哈希散列的多个字段的值
List<String> list = stringHashOperations.multiGet("user", Arrays.asList("name", "age", "email", "address"));
for (String s : list) {
System.out.println(s);
}
// HGETALL 获取哈希散列的所有字段与值
Map<String, String> map = stringHashOperations.entries("user");
for (String s : map.keySet()) {
System.out.println(s + " " + map.get(s));
}
// HKEYS 获取哈希散列的所有字段
Set<String> keys = stringHashOperations.keys("user");
for (String key : keys) {
System.out.println(key);
}
// HVALS 获取哈希散列的所有值
List<String> values = stringHashOperations.values("user");
for (String value : values) {
System.out.println(value);
}
// 删除数据
redisTemplate.delete("user");
}
@Test
@Order(3)
void other() {
// 准备数据
stringHashOperations.putAll("user", Map.of("id", "1", "name", "tom", "email", "[email protected]", "age", "18"));
// HEXISTS 哈希散列是否存在指定字段
Boolean hasName = stringHashOperations.hasKey("user", "name");
assertTrue(hasName);
// HDEL 删除哈希散列的指定字段
Long delete = stringHashOperations.delete("user", "id", "email");
assertEquals(2, delete);
// HLEN 哈希散列的长度
Long size = stringHashOperations.size("user");
assertEquals(2, size);
// HSTRLEN 哈希散列的字段的长度
Long length = stringHashOperations.lengthOfValue("user", "name");
assertEquals(3, length);
// HINCRBY 增减哈希散列中指定字段的值
Long increment = integerHashOperations.increment("user", "age", 1);
assertEquals(19, increment);
// 删除数据
redisTemplate.delete("user");
}
}
ZSetOperations
Zset 有序集合(Sorted Sets)
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Resource;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ZSetOperations;
/**
* ZSet 有序集合(Sorted Sets)
*/
@SpringBootTest
public class ZSetOperationsTest {
@Autowired
private RedisOperations<String, Object> redisOperations;
/**
* 值是String类型的有序集合
*
* 自定义的RedisTemplate在格式化String时会多出引号
*
* 需要使用Spring内置的StringRedisTemplate
*/
@Resource(name = "stringRedisTemplate")
private ZSetOperations<String, String> stringZSetOperations;
/**
* ZADD 将一个或多个member及source添加到有序集合中
*/
@Test
@Order(1)
void add() {
Boolean flag = stringZSetOperations.add("zset", "maths", 99);
assertTrue(flag);
Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
set.add(new DefaultTypedTuple<>("chinese", 90.0));
set.add(new DefaultTypedTuple<>("english", 60.0));
Long add = stringZSetOperations.add("zset", set);
assertEquals(2, add);
// 删除数据
redisOperations.delete("zset");
}
/**
* 根据指定规则排序取出
*/
@Test
@Order(2)
void get() {
// 准备数据
Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
set.add(new DefaultTypedTuple<>("chinese", 115.0));
set.add(new DefaultTypedTuple<>("maths", 135.5));
set.add(new DefaultTypedTuple<>("english", 110.5));
set.add(new DefaultTypedTuple<>("physics", 88.0));
set.add(new DefaultTypedTuple<>("chemistry", 65.0));
stringZSetOperations.add("zset", set);
/// 正序
// ZRANGE 返回排序集合中的指定范围的元素(正序,从小到大)
Set<String> zset = stringZSetOperations.range("zset", 0, -1);
System.out.println(zset);
// ZRANGEBYSCORE 返回指定分数内的元素(正序,从小到大)
Set<String> zset3 = stringZSetOperations.rangeByScore("zset", 10, 100);
System.out.println(zset3);
// ZRANGEBYSCORE 返回指定分数内的元素(正序,从小到大)(LIMIT 分页)
Set<String> zset5 = stringZSetOperations.rangeByScore("zset", 0, 200, 1, 2);
System.out.println(zset5);
// ZRANGE xxxWithScores 返回时包含分数(正序,从小到大)其他同
Set<ZSetOperations.TypedTuple<String>> zset2 = stringZSetOperations.rangeWithScores("zset", 0, -1);
if (zset2 != null) {
for (ZSetOperations.TypedTuple<String> stringTypedTuple : zset2) {
System.out.println(stringTypedTuple.getValue() + " " + stringTypedTuple.getScore());
}
}
/// 倒序
// ZREVRANGE 返回排序集合中的指定范围的元素(倒序,从大到小)
stringZSetOperations.reverseRange("zset", 0, -1);
// ZREVRANGEBYSCORE 返回指定分数内的元素(倒序,从大到小)
stringZSetOperations.reverseRangeByScore("zset", 10, 100);
// ZREVRANGEBYSCORE 返回指定分数内的元素(倒序,从大到小)(LIMIT 分页)
stringZSetOperations.reverseRangeByScore("zset", 0, 200, 1, 2);
// ZREVRANGE xxxWithScores 返回时包含分数(倒序,从大到小)其他同
stringZSetOperations.reverseRangeWithScores("zset", 0, -1);
// 删除数据
redisOperations.delete("zset");
}
@Test
@Order(3)
void other() {
// 准备数据
Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
set.add(new DefaultTypedTuple<>("chinese", 115.0));
set.add(new DefaultTypedTuple<>("maths", 135.5));
set.add(new DefaultTypedTuple<>("english", 110.5));
set.add(new DefaultTypedTuple<>("physics", 88.0));
set.add(new DefaultTypedTuple<>("chemistry", 65.0));
stringZSetOperations.add("zset", set);
// ZSCORE 返回排序集中元素的分数
Double score = stringZSetOperations.score("zset", "chinese");
assertEquals(115.0, score);
// 查询元素在有序集合中的排名
// ZRANK
Long rank = stringZSetOperations.rank("zset", "chinese");
assertEquals(3, rank);
// ZREVRANK
Long reverseRank = stringZSetOperations.reverseRank("zset", "chinese");
assertEquals(1, reverseRank);
// ZCARD key 返回排序集合的元素数量
Long size = stringZSetOperations.size("zset");
assertEquals(5, size);
// ZCOUNT 统计指定范围内的元素数量
Long count = stringZSetOperations.count("zset", 0, 100);
assertEquals(2, count);
// ZINCRBY 给指定元素增减分数
Double incrementScore = stringZSetOperations.incrementScore("zset", "english", 18.5);
assertEquals(129.0, incrementScore);
// ZREM 删除元素
Long remove = stringZSetOperations.remove("zset", "chinese", "english");
assertEquals(2, remove);
// 删除数据
redisOperations.delete("zset");
}
}
Transactions
事务
import java.util.Arrays;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.ValueOperations;
/**
* 事务
*/
@SpringBootTest
public class TransactionsTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 值是数字类型的字符串
*
* 自定义的RedisTemplate可以格式化Integer
*/
@Resource(name = "redisTemplate")
private ValueOperations<String, Integer> valueOperations;
@Test
void test() {
valueOperations.set("a", 100);
valueOperations.set("b", 100);
// 乐观锁
// redisOperations.watch("a");
List<Long> execute = redisTemplate.execute(new SessionCallback<>() {
@Override
public List<Long> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
ValueOperations<String, Long> valueOperations = operations.opsForValue();
valueOperations.increment("a", 10);
valueOperations.decrement("b", 10);
return operations.exec();
}
});
Assertions.assertEquals(110, execute.get(0));
Assertions.assertEquals(90, execute.get(1));
// 删除数据
redisTemplate.delete(Arrays.asList("a", "b"));
}
}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK