SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis。
官网地址:https://spring.io/projects/spring-data-redis
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--jedis或者redis底层都会基于commons库来实现连接池效果-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
server.port=8087
spring.redis.host=127.0.0.1
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=123456
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=8
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=30000
=====另一种配置方式======
spring:
redis:
host: 127.0.0.1 # ip
port: 6379 # 端口
password: 123456 # 密码
lettuce: # 默认的客户端是lettuce,若要使用jedis需要添加依赖
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 100ms # 连接等待时间
database: 0 # 选择库
@SpringBootTest
class RedisTemplateApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
redisTemplate.opsForValue().set("name","李四");
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}
=========输出结果========
name = 李四
现象: 在控制台能够看见正常的key和value,但是我们通过redis客户端查看时发现结果是这样的。
原因: RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化;
通过查看RedisTemplate源码可以发现常见的类型都实现了序列化:
查看源码RedisTemplate可以发现默认是采用JDK序列化;这种方式会在写入前把Object序列化为字节的形式,然后进行存储,缺点很明显:可读性差、占用内存。所以我们需要修改默认的JDK序列化方式:
我们希望在redis中存的,所见及所得,而不是进行jdk序列化之后的样子,所以我们需要改变序列化方式。
如上图所示,是主要的集中序列化方式。其中,key为字符串的时候我们通常使用StringRedisSerializer方式,value为字符串的时候,通常使用GenericJackson2JsonRedisSerializer方式。
1、配置Json序列化的依赖Jackson:
<!-- jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
2、RedisConfig配置类:
@Configuration
public class RedisConfig {
/**
* 描述信息: key和hashKey采用string序列化方式 alue和hashValue采用Json序列化方式
*
* @date 2023/05/17
* @param redisConnectionFactory
* @return org.springframework.data.redis.core.RedisTemplate
**/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//key和hashKey采用string序列化方式
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
//value和hashValue采用Json序列化方式
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
@Test
void testSaveUser() {
redisTemplate.opsForValue().set("user:100",new User("小红",29));
User user = (User) redisTemplate.opsForValue().get("user:100");
System.out.println("user = " + user);
}
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
当我们传入的Value为实体类对象的时候,会用 GenericJackson2JsonRedisSerializer序列化器把java对象转为JSON格式,然后再存入Redis库中,在我们使用redisTemplate.opsForValue().get方法获取数据时,通过存入的@class属性,把JSON反序列化成JAVA对象。
这样一来我们在IDEA的控制器上很直观的就可以看到数据,但是也存在了一个缺点->浪费内存,因为我们要存放@class这一段额外的数据来反序列化JSON字符串。
为了节省内存空间,我们一般实际中并不会使用JSON序列化器(GenericJackson2JsonRedisSerializer序列化器)来处理value,而是统一使用String序列化器(RedisSerializer.string序列化器),要求只能存储String类型的key和value。我们只需要在存入数据时,手动的把JAVA对象转变为JSON格式字符串,然后取数据时,再把JSON转回JAVA对象就好了。
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
@SpringBootTest
class RedisStringRTDemoApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testObject() throws JsonProcessingException {
// 1.创建对象
Student s1 = new Student("李四", '男', 22);
// 2.手动序列化
String json = mapper.writeValueAsString(s1);
// 3.存储一条数据
stringRedisTemplate.opsForValue().set("student:2", json);
// 4.获取数据
String jsonStudent = stringRedisTemplate.opsForValue().get("student:2");
// 5.手动反序列化
Student s = mapper.readValue(jsonStudent, Student.class);
System.out.println("s:" + s);
}
}
参考链接:https://blog.csdn.net/weixin_43811057/article/details/130735304
新增一个字符串类型的值var1是keyvar2是值key存在就覆盖,不存在新增
redisTemplate.opsForValue().set("BBB","你好");
新增一个字符串类型的值,同时设置过期时间var1是keyvar2是值key存在就覆盖,不存在新增
redisTemplate.opsForValue().set("BBB","你好", Duration.ofMinutes(1));
新增一个字符串类型的值,同时设置过期时间var1是key,var2是值,key存在就覆盖,不存在新增
redisTemplate.opsForValue().set("BBB","你好", 1, TimeUnit.MINUTES);
TimeUnit.DAYS //天
TimeUnit.HOURS //小时
TimeUnit.MINUTES //分钟
TimeUnit.SECONDS //秒
TimeUnit.MILLISECONDS //毫秒
给对应的key追加value,key不存在直接新增
redisTemplate.opsForValue().append("AAA", "哈哈哈");
将key的值从下标1往后替换为新的value,key不存在相当于新增
redisTemplate.opsForValue().set("BBB","您的",1);
key键对应的值value对应的ascii码,在offset的位置(从左向右数)变为value
redisTemplate.opsForValue().setBit("BBB", 0, true);
判断指定的位置ASCII码的bit位是否为1
redisTemplate.opsForValue().getBit("BBB", 1);
如果key不存在则新增,存在则不改变已经有的值
redisTemplate.opsForValue().setIfAbsent("BBB", "好的");
如果key不存在则新增,存在则不改变已经有的值,同时设置过期时间
redisTemplate.opsForValue().setIfAbsent("AAA", "好的", 1, TimeUnit.MINUTES);
如果key不存在则新增,存在则不改变已经有的值,同时设置过期时间
redisTemplate.opsForValue().setIfAbsent("BBB", "好的", Duration.ofMinutes(1));
如果key存在则修改,不存在则不改变已经有的值
redisTemplate.opsForValue().setIfPresent("BBB", "好的");
如果key存在则修改,不存在则不改变已经有的值,同时设置过期时间
redisTemplate.opsForValue().setIfPresent("BBB", "好的",1, TimeUnit.MINUTES);
如果key存在则修改,不存在则不改变已经有的值,同时设置过期时间
redisTemplate.opsForValue().setIfPresent("BBB", "好的",Duration.ofMinutes(1));
获取key对应的值,如果key存在则修改,不存在则新增
redisTemplate.opsForValue().getAndSet("BBB", "心情");
以增量的方式(默认增量为1)将long值存储在变量中(value为其他类型时报错),返回最新值
redisTemplate.opsForValue().increment("AAA");
以指定增量的方式将Long值存储在变量中,返回最新值
redisTemplate.opsForValue().increment("AAA",2);
以指定增量的方式将Double值存储在变量中,返回最新值
redisTemplate.opsForValue().increment("AAA", 3.2);
以递减的方式(默认为1)将long值存储在变量中(value为其他类型时报错,Double也不行,只能为Long),返回最新值
redisTemplate.opsForValue().decrement("AAA");
以指定递减量递减的方式将long值存储在变量中(value为其他类型时报错,Double也不行,只能为Long),返回最新值
redisTemplate.opsForValue().decrement("AAA",2);
获取指定key对应值的长度
redisTemplate.opsForValue().size("BBB");
获取指定的key对应的值
String BBB = (String) redisTemplate.opsForValue().get("BBB");
System.out.println("BBB = " + BBB);
获取key指定下标之间对应的值
String BBB = redisTemplate.opsForValue().get("BBB",0,1);
System.out.println("BBB = " + BBB);
将map中的key分别作为不同的key存到Redis中(见截图)
若某个key已经存在则替换为新值,其他不存在的则新增
map中5个key,3个存在Redis中,2个没有,结果就是3个值被修改,2个新增
Map valueMap = new HashMap();
valueMap.put("valueMap1","aa");
valueMap.put("valueMap2","bb");
valueMap.put("valueMap3","cc");
valueMap.put("valueMap4","ee");
redisTemplate.opsForValue().multiSet(valueMap);
将map中的key分别作为不同的key存到Redis中
若某个key已经存在不做修改,不存在的则新增(map中的key在Redis中都不存在时才新增)
map中5个key,3个存在Redis中,2个没有,结果就是不会新增不会修改,若map中5个key,5个都不存在Redis中,则新增
Map valueMap = new HashMap();
valueMap.put("valueMap1","aa");
valueMap.put("valueMap2","bb");
valueMap.put("valueMap3","cc");
valueMap.put("valueMap4","ee");
valueMap.put("valueMap5","ff");
redisTemplate.opsForValue().multiSetIfAbsent(valueMap);
根据集合中的key取出对应的value值
List paraList = new ArrayList();
paraList.add("valueMap1");
paraList.add("valueMap2");
paraList.add("valueMap3");
List list = redisTemplate.opsForValue().multiGet(paraList);
对象和对象集合一定要转成JSON存放,容易解析
List<MPEntity> list = mpService.list();
redisTemplate.opsForValue().set("BBB", JSON.toJSONString(list));
获取解析JSON字符串
String bbb = (String) redisTemplate.opsForValue().get("BBB");
List<MPEntity> mpEntities = JSON.parseArray(bbb, MPEntity.class);
System.out.println("mpEntities = " + mpEntities);
参考链接:https://blog.csdn.net/qq_37131747/article/details/125673505
redisTemplate.opsForValue().type(key);
public void renameKey(String oldKey, String newKey) {
redisTemplate.opsForValue().rename(oldKey, newKey);
}
public Boolean renameOldKeyIfAbsent(String oldKey, String newKey) {
return redisTemplate.opsForValue().renameIfAbsent(oldKey, newKey);
}
redisTemplate.hasKey(key)
redisTemplate.delete(key)
批量删除key:
redisTemplate.delete(keys) //其中keys:Collection keys
public Boolean expire(String key, long timeout, TimeUnit unit) {
return redisTemplate.expire(key, timeout, unit);
}
public Boolean expireAt(String key, Date date) {
return redisTemplate.expireAt(key, date);
}
redisTemplate.getExpire(key);
//返回剩余过期时间并且指定时间单位
public Long getExpire(String key, TimeUnit unit) {
return redisTemplate.getExpire(key, unit);
}
public Set<String> getPatternKey(String pattern) {
return redisTemplate.keys(pattern);
}
public Boolean persistKey(String key) {
return redisTemplate.persist(key);
}
public Boolean moveToDbIndex(String key, int dbIndex) {
return redisTemplate.move(key, dbIndex);
}
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。
redisTemplate.opsForHash().get(key, field)
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
redisTemplate.opsForHash().put(key, hashKey, value)
public void hPutAll(String key, Map<String, String> maps) {
redisTemplate.opsForHash().putAll(key, maps);
}
public Boolean hashPutIfAbsent(String key, String hashKey, String value) {
return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
}
public Long hashDelete(String key, Object... fields) {
return redisTemplate.opsForHash().delete(key, fields);
}
public boolean hashExists(String key, String field) {
return redisTemplate.opsForHash().hasKey(key, field);
}
public Long hashIncrBy(String key, Object field, long increment) {
return redisTemplate.opsForHash().increment(key, field, increment);
}
public Double hIncrByDouble(String key, Object field, double delta) {
return redisTemplate.opsForHash().increment(key, field, delta);
}
redisTemplate.opsForHash().keys(key)
public List<Object> hValues(String key) {
return redisTemplate.opsForHash().values(key);
}
redisTemplate.opsForHash().size(key)
public Cursor<Entry<Object, Object>> hashScan(String key, ScanOptions options) {
return redisTemplate.opsForHash().scan(key, options);
}
redisTemplate.opsForList().index(key, index)
redisTemplate.opsForList().range(key, start, end)
redisTemplate.opsForList().leftPush(key, value)
redisTemplate.opsForList().leftPushAll(key, value)
redisTemplate.opsForList().leftPushIfPresent(key, value)
redisTemplate.opsForList().rightPush(key, value)
redisTemplate.opsForList().rightPushAll(key, value)
redisTemplate.opsForList().set(key, index, value)
redisTemplate.opsForList().leftPop(key)
redisTemplate.opsForList().leftPop(key, timeout, unit)
redisTemplate.opsForList().rightPop(key)
redisTemplate.opsForList().rightPop(key, timeout, unit)
redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey)
redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit)
(index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; index<0, 从尾部开始删除第一个值等于value的元素)
redisTemplate.opsForList().remove(key, index, value)
redisTemplate.opsForList().trim(key, start, end)
redisTemplate.opsForList().size(key)
redisTemplate.opsForSet().add(key, values)
redisTemplate.opsForSet().remove(key, values)
redisTemplate.opsForSet().size(key)
redisTemplate.opsForSet().isMember(key, value)
(key对应的无序集合与otherKey对应的无序集合求交集)
redisTemplate.opsForSet().intersect(key, otherKey)
//获取多个集合的交集(Collection var2)
redisTemplate.opsForSet().intersect(key, otherKeys)
redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey)
//key集合与多个集合的交集存储到destKey无序集合中
redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey)
redisTemplate.opsForSet().union(key, otherKeys)
redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey)
redisTemplate.opsForSet().difference(key, otherKeys)
redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey)
redisTemplate.opsForSet().members(key)
redisTemplate.opsForSet().randomMembers(key, count)
//随机获取集合中的一个元素
redisTemplate.opsForSet().randomMember(key)
redisTemplate.opsForSet().scan(key, options)
ZSetOperations提供了一系列方法对有序集合进行操作
redisTemplate.opsForZSet().add(key, value, score)
redisTemplate.opsForZSet().remove(key, values)
redisTemplate.opsForZSet().incrementScore(key, value, delta)
redisTemplate.opsForZSet().rank(key, value)
redisTemplate.opsForZSet().reverseRank(key, value)
redisTemplate.opsForZSet().reverseRangeWithScores(key, start,end)
redisTemplate.opsForZSet().reverseRangeByScore(key, min, max)
//从高到低的排序集中获取分数在最小和最大值之间的元素
redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, start, end)
redisTemplate.opsForZSet().count(key, min, max)
redisTemplate.opsForZSet().size(key)
redisTemplate.opsForZSet().score(key, value)
redisTemplate.opsForZSet().removeRange(key, start, end)
redisTemplate.opsForZSet().removeRangeByScore(key, min, max)
redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey)
redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey)
Cursor<TypedTuple<Object>> scan = opsForZSet.scan("test3", ScanOptions.NONE);
while (scan.hasNext()){
ZSetOperations.TypedTuple<Object> item = scan.next();
System.out.println(item.getValue() + ":" + item.getScore());
}
参考链接:https://blog.csdn.net/lanfeng_lan/article/details/121152461