Redis5:Redis的Java客户端——Jedis与SpringDataRedis详解_java redis
目录
1、Jedis客户端
1.1使用过程
2、SpringDataRedis
2.1 SpingDataRedis介绍
2.2SpringDataRedis快速入门
2.3RedisTemplate的RedisSerializer
2.3.1RedisTemplate中JDK序列化局限性
2.3.2方式一:改变RedisTemplate的序列化方式
2.3.3RedisTemplate存储一个对象
2.3.4 方式二:StringRedisTemplate
1、Jedis客户端
1.1使用过程
1、新建一个maven项目
2、引入依赖
redis.clients jedis 3.7.0 org.junit.jupiter junit-jupiter RELEASE test
3、建立连接
@BeforeEachvoid setUp(){ //1、建立连接 jedis = new Jedis(\"120.53.240.101\",6379); //2、设置密码 jedis.auth(\"123321\"); //3、选择库 jedis.select(5);}
注意!!Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式
package com.tang.util;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;public class JedisConnectionFactory { // Jedis连接池对象 private static JedisPool jedisPool; static { //配置连接池 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 最大连接数 jedisPoolConfig.setMaxTotal(8); // 最大空闲连接数,最多可创建的连接 jedisPoolConfig.setMaxIdle(8); // 最小空闲连接数,最少可创建的连接数,长时间没链接时就销毁至0,当然也可以预备一些连接 jedisPoolConfig.setMinIdle(0); //默认值是-1,表示没有限制 // 设置最长等待时间(ms),连接池里没有连接时就等待,超过这个时间就抛异常 jedisPoolConfig.setMaxWaitMillis(1000); //创建连接池对象,1000是连接超时时间 jedisPool = new JedisPool(jedisPoolConfig,\"120.53.240.101\", 6379, 1000, \"123321\"); } // 获取Jedis实例 public static Jedis getJedis() { return jedisPool.getResource(); }}
在测试类中,连接直接从工具类里拿
@BeforeEachvoid setUp(){ //1、建立连接 jedis = JedisConnectionFactory.getJedis(); //2、选择库 jedis.select(6);}
4、测试string
set类型
@Testvoid testString(){ //存入数据 String result = jedis.set(\"name\",\"tang\"); System.out.println(\"result=\"+result); //获取数据 String name = jedis.get(\"name\"); System.out.println(\"name=\"+name);}
Hash类型
@Testvoid testHash(){ jedis.hset(\"user\",\"name\",\"tang\"); jedis.hset(\"user\",\"age\",\"22\"); Map user = jedis.hgetAll(\"user\"); System.out.println(user);}
5、释放资源
@AfterEachvoid tearDown(){ //健壮性判断,关闭链接 if(jedis != null){ jedis.close(); }}
6、结果返回
string
hash
2、SpringDataRedis
2.1 SpingDataRedis介绍
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,
官网Spring Data Redis
-
提供了对不同Redis客户端的整合(Lettuce和jedis)
-
提供了RedisTemplate统一API来操作Redis
-
支持Redis的发布订阅模型
-
支持Redis哨兵和Redis集群
-
支持基于Lettuce的响应式编程
-
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
2.2SpringDataRedis快速入门
SPringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单:
新建项目,我们在这创建一个SpringBoot项目
可以提前勾选几个依赖。(注意!!在我这,这个SpringBoot版本进去要改一下,不然会因为版本过高等问题出错)
1、导入依赖
org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 2.11.1
2、配置文件
spring: redis: host: 120.53.240.101 port: 6379 password: 123321 lettuce: pool: max-active: 8 #最大连接 max-idle: 8 #最大空闲连接 min-idle: 0 #最小空闲连接 max-wait: 100 #最大等待时间
3、注入RedisTemplate
@Autowiredprivate RedisTemplate redisTemplate;
4、编写测试
在这里我们看到,set()并没有强硬要求我们必须传入字符串参数
@Testvoid testString() { //写入一条String类型数据 redisTemplate.opsForValue().set(\"name\",\"tang\"); //读取一条String类型数据 Object name = redisTemplate.opsForValue().get(\"name\"); System.out.println(\"name=\"+name);}
2.3RedisTemplate的RedisSerializer
2.3.1RedisTemplate中JDK序列化局限性
当我们尝试用SpringDataRdies修改某个键的值时,会发现如下情景:
我们先在数据库中存入键name,并把他的value设为Jack
然后我们调用SpringDataRedis,将键name的value设置为tang
得到如下返回结果
回到我们的redis命令行去get name,
我们会发现name的值并未改变
获取一下全部的keys,出现一串不知名的key。
我们来get一下这个key,返回的还是一串字符
为什么会这样子?
我们看set方法我们发现他接受的并不是字符串,它可以接收任何类型的对象,然后转换成redis可以处理的字节(JDK的序列化工具ObjectOutputStream)再写入redis。
debug进入set方法,我们发现它最终进入到了JdkSerializer
最后调用ObjectOutputStream
这样写有什么问题?
可读性差
内存占用较大
容易出现bug:不是修改,因为key也被序列化
2.3.2方式一:改变RedisTemplate的序列化方式
crtl+H查看RedisSerializer的subTypes
JdkSerializationRedisSerializer我们已经发现它并不好用。
StringRedisSerializer是专门处理字符串的序列化工具getbytes:当key或者hashkey是字符串类型的时候可以使用。
GenericJackson2JsonRedisSerializer这是一个转JSON字符串的序列化工具:适合于value使用
我们手写一个工具类,自定义RedisTemplate的序列化方式
package com.tang.springdataredisdemo.config;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.GenericJackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializer;import sun.net.www.content.text.Generic;/** * 注册一个配置类,实现自定义Redis序列化 * @author tang * @date 2021-06-07 16:05 * @desc */@Configurationpublic class RedisConfig { @Bean // 自定义Redis序列化 public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { //创建RedisTemplate对象 RedisTemplate template = new RedisTemplate(); //设置连接工厂 template.setConnectionFactory(connectionFactory); //创建JSON的序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); //设置key序列化方式 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); //设置value序列化方式 template.setValueSerializer(jsonRedisSerializer); template.setHashValueSerializer(jsonRedisSerializer); //返回RedisTemplate对象 return template; }}
添加这个配置类后,我们对测试的部分进行一些修改,改为泛型格式
@Autowiredprivate RedisTemplate redisTemplate;
还需要添加一个依赖
com.fasterxml.jackson.core jackson-databind
idea里结果返回如下:
现在去redis数据库里看一下,没问题了!
2.3.3RedisTemplate存储一个对象
创建一个User类
package com.tang.springdataredisdemo.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructorpublic class User { private String name; private Integer age;}
编写测试案例
@Testvoid testObject() { User user = new User(\"tang\", 18); //写入一条Object类型数据 redisTemplate.opsForValue().set(\"user:100\",user); //读取一条Object类型数据(直接强转) User user1 =(User) redisTemplate.opsForValue().get(\"user:100\"); System.out.println(\"user=\"+user1);}
结果返回如下
在数据库中,自动转化为JSON风格的数据存储起来
图片上的JSON中还有一个“@class”,这个字段帮助将json格式反序列化为对应的对象(反射)
但这会带来额外的内存开销
2.3.4 方式二:StringRedisTemplate
在2.3.3中我们发现使用GenericJackson2JsonRedisSerializer会产生额外的字段,消耗了很大一部分的内存空间。
为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
Spring默认提供了一个StringRedisTemplate类,他的key和value的序列化方式默认就是String方式。省去了我们自定义的时间。
在测试类注入
@Autowiredprivate StringRedisTemplate stringRedisTemplate;
修改测试用例,在存入对象的部分多了一个手动序列化和反序列化的过程
@Testvoid testString() { //写入一条String类型数据 stringRedisTemplate.opsForValue().set(\"name\",\"tang\"); //读取一条String类型数据 Object name = stringRedisTemplate.opsForValue().get(\"name\"); System.out.println(\"name=\"+name);}@Testvoid testHash(){ stringRedisTemplate.opsForHash().put(\"user:200\",\"name\",\"tang\"); stringRedisTemplate.opsForHash().put(\"user:200\",\"age\",\"18\"); Map entries = stringRedisTemplate.opsForHash().entries(\"user:200\"); System.out.println(\"entries=\"+entries);}//序列化工具,使用fastjson也是可以的private static final ObjectMapper objectMapper = new ObjectMapper();@Testvoid testObject() throws JsonProcessingException { //创建对象 User user = new User(\"tang\", 18); //手动序列化 String json = objectMapper.writeValueAsString(user); //写入一条Object类型数据 stringRedisTemplate.opsForValue().set(\"user:100\",json); //读取一条Object类型数据(直接强转) String jsonUser =stringRedisTemplate.opsForValue().get(\"user:100\"); //手动反序列化 objectMapper.readValue(jsonUser,User.class); System.out.println(\"user=\"+jsonUser);}
结果显示