1. Redis的Java客户端
2. Jedis
2.1 Jedis快速入门
Jedis的官网地址: https://github.com/redis/jedis,我们先来个快速入门:
2.1.1 引入依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.0</version>
</dependency>
<!-- 为了方便测试 引入单元测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.3 建立连接并测试
TestJedis.java
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.List;
/**
* ClassName: TestJedis
* Description: Jedis快速入门测试程序
*
* @author CodeJiao
* @date 2022/4/30 11:42
*/
public class TestJedis {
private Jedis jedis;
@Before // 在@Test执行之前进行jedis的初始化
public void initJedis() {
// 建立连接
jedis = new Jedis("192.168.135.130", 6379);
// 设值密码
jedis.auth("317525");
// 选择库(默认选中0)
jedis.select(0);
}
@After // 在@Test执行之后进行jedis的资源释放
public void releaseSources() {
// 释放资源
if (jedis != null) {
jedis.close();
}
}
/**
* 测试String数据类型
*/
@Test
public void testString() {
// 插入数据,方法名称就是redis命令名称,非常简单
String result = jedis.set("name", "codejiao");
if (result.equals("OK")) {
System.out.println("插入name数据成功");
}
// 获取数据
String name = jedis.get("name");
System.out.println("name: " + name);
}
/**
* 测试Hash数据类型
*/
@Test
public void testHash() {
// 设置数据
HashMap<String, String> map = new HashMap<>();
map.put("name", "Jack");
map.put("age", "18");
map.put("gender", "male");
String result = jedis.hmset("user", map);
if (result.equals("OK")) {
System.out.println("插入user数据成功");
}
// 获取数据
List<String> user = jedis.hmget("user", "name", "age", "gender");
System.out.println(user);
}
}
testString运行结果:
testHash运行结果:
2.2 Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们大家使用Jedis连接池代替Jedis的直连方式。
2.2.1 配置连接池
JedisConnectionFactory.java
package config;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* ClassName: JedisConnectionFactory
* Description: Jedis连接池配置类
*
* @author CodeJiao
* @date 2022/4/30 21:20
*/
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大连接
jedisPoolConfig.setMaxTotal(8);
// 最大空闲连接
jedisPoolConfig.setMaxIdle(8);
// 最小空闲连接
jedisPoolConfig.setMinIdle(0);
// 设置最长等待时间, ms
jedisPoolConfig.setMaxWaitMillis(200);
// 初始化Jedis连接池 1000是超时时间
jedisPool = new JedisPool(jedisPoolConfig, "192.168.135.130", 6379, 1000, "317525");
}
/**
* 获取Jedis连接对象
*/
public static Jedis getJedis() {
return jedisPool.getResource();
}
}
2.2.2 从连接池获取Jedis对象
testString运行结果:
testHash运行结果:
说明:
3. SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
特性:
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
3.1 RedisTemplate快速入门
3.1.1 创建项目
- 选择创建SpringBoot项目
- 填写项目的基本信息
- 选择一个
Lombok
依赖和一个Spring Data Redis
依赖 - 点击Finish即可完成创建
- 删除多余的东西,让项目看起来更加清爽
3.1.2 引入依赖
<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
3.1.3 配置redis
application.yml
spring:
redis:
host: 192.168.135.130
port: 6379
password: 317525
lettuce:
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 1000 # 连接等待时间
3.1.4 编写代码进行测试
RedisDemoApplicationTests.java
package com.tian.redisdemo;
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;
@SpringBootTest
class RedisDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
// 插入一条string类型数据
redisTemplate.opsForValue().set("name", "CodeJiao");
// 读取一条string类型数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}
运行结果:
4. SpringDataRedis的序列化方式
就上面演示的案例来说:
RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
缺点:
- 可读性差
- 内存占用较大
4.1 自定义RedisTemplate的序列化方式
我们可以自定义RedisTemplate的序列化方式,代码如下:
RedisConfig.java
package com.tian.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;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key和 hashKey采用 string序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// value和 hashValue采用 JSON序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回RedisTemplate
return template;
}
}
由于用到了Jackson,所以我们需要在pom文件中引入相关的依赖:
<!--引入Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
重新开始测试:
运行结果:
4.2 测试序列化自定义User对象
4.2.1 新建一个User类
User.java
package com.tian.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private Integer age;
}
4.2.2 编写测试方法
RedisDemoApplicationTests.java
@Test
void testSaveUser() {
// 写入数据
redisTemplate.opsForValue().set("user:01", new User("Jack", 19));
// 获取数据
User user = (User) redisTemplate.opsForValue().get("user:01");
System.out.println(user.toString());
}
运行结果:
4.3 StringRedisTemplate
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
现在的测试代码:
package com.tian;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tian.pojo.User;
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.StringRedisTemplate;
import java.util.Map;
@SpringBootTest
class RedisDemoApplicationTests {
@Autowired
private StringRedisTemplate redisTemplate;
@Test
void testString() {
// 插入一条string类型数据
redisTemplate.opsForValue().set("name", "CodeJiao");
// 读取一条string类型数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
// ObjectMapper是SpringMVC默认的序列化工具, 你也可以使用诸如FastJson之类的序列化工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("Rose", 21);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
redisTemplate.opsForValue().set("user:200", json);
// 获取数据
String jsonUser = redisTemplate.opsForValue().get("user:200");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
@Test
void testHash() {
// 存入数据
redisTemplate.opsForHash().put("user:02", "name", "Mac");
redisTemplate.opsForHash().put("user:02", "age", "99");
// 取出数据
Map<Object, Object> entries = redisTemplate.opsForHash().entries("user:02");
System.out.println(entries);
}
}
运行结果:
testString()
运行结果:
testSaveUser()
运行结果:
testHash()
运行结果:
4.4 小结