当前位置: 首页 > backend >正文

Redis的Java客户端

目录

Jedis客户端

快速入门

引入依赖:

建立连接

测试

释放资源

完整代码

连接池

SpringDataRedis客户端

快速入门

引入依赖

配置Redis

注入RedisTemplate

编写测试

测试结果

自定义序列化

StringRedisTemplate


在Redis官网中提供了各种语言的客户端,地址:Redis客户端下载https://www.redis.net.cn/clients/

其中Java客户端也包含很多:

标记为*的就是推荐使用的java客户端,包括:

  • Jedis和Lettuce:这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。

  • Redisson:是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。

Jedis客户端

Jedis的官网地址: https://github.com/redis/jedis

快速入门

我们先来个快速入门,首先创建一个maven项目:

引入依赖:

<!--jedis-->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>
<!--单元测试-->
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>

建立连接

新建一个单元测试类,内容如下:

	private Jedis jedis;@BeforeEachvoid setUp() {//1.建立连接jedis = new Jedis("192.168.200.130", 6379);//2.设置密码jedis.auth("123456");//3.选择数据库jedis.select(0);}

测试

	@Testvoid testString() {//1.添加String result = jedis.set("name", "tom");System.out.println(result);//2.查询String name = jedis.get("name");System.out.println(name);}@Testvoid testHash() {//添加jedis.hset("user:10001", "name", "tashuo");jedis.hset("user:10001", "age", "21");//查询Map<String, String> map = jedis.hgetAll("user:10001");System.out.println(map);}

释放资源

	@AfterEachvoid tearDown() {if(jedis != null) {jedis.close();}}

完整代码

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;import java.util.Map;public class JedisTest {private Jedis jedis;@BeforeEachvoid setUp() {//1.建立连接jedis = new Jedis("192.168.200.130", 6379);//2.设置密码jedis.auth("123456");//3.选择数据库jedis.select(0);}@Testvoid testString() {//1.添加String result = jedis.set("name", "tom");System.out.println(result);//2.查询String name = jedis.get("name");System.out.println(name);}@Testvoid testHash() {//添加jedis.hset("user:10001", "name", "tashuo");jedis.hset("user:10001", "age", "21");//查询Map<String, String> map = jedis.hgetAll("user:10001");System.out.println(map);}@AfterEachvoid tearDown() {if(jedis != null) {jedis.close();}}
}

连接池

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。

import redis.clients.jedis.*;public class JedisConnectionFactory {private static JedisPool jedisPool;static {// 配置连接池JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(8);// 最大连接数poolConfig.setMaxIdle(8);// 最大空闲连接数poolConfig.setMinIdle(0);// 最小空闲连接数poolConfig.setMaxWaitMillis(1000);// 最大等待时间// 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码jedisPool = new JedisPool(poolConfig, "192.168.200.130", 6379, 1000, "123456");}// 获取Jedis对象public static Jedis getJedis(){return jedisPool.getResource();}
}

然后,我们需要对刚刚的测试代码进行修改,修改为通过连接池获取对象的写法:

首先,就是修改获取Jedis对象的方式,由构造方法获取改为通过连接池获取:

	@BeforeEachvoid setUp() {//1.建立连接//通过构造方法创建Jedis对象//jedis = new Jedis("192.168.200.130", 6379);//通过静态方法创建Jedis对象(通过连接池获取对象)jedis = JedisConnectionFactory.getJedis();//2.设置密码jedis.auth("123456");//3.选择数据库jedis.select(0);}

获取的Jedis对象不再需要销毁,而是使用完之后归还到连接池中,

	@AfterEachvoid tearDown() {if(jedis != null) {jedis.close();}}

代码还是那段代码,只是底层方法会判断你这个Jedis对象是通过构造方法创建的还是从连接池获取的:如果是根据构造方法生成的,会进行销毁;如果是从连接池获取的,使用完之后会归还到连接池中。底层方法如下:

    public void close() {if (this.dataSource != null) {JedisPoolAbstract pool = this.dataSource;this.dataSource = null;if (this.isBroken()) {pool.returnBrokenResource(this);} else {pool.returnResource(this);}} else {super.close();}}

测试,查看效果,可知通过连接池获取Jedis对象的操作成功:

SpringDataRedis客户端

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:Spring Data Redis

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)

  • 提供了RedisTemplate统一API来操作Redis

  • 支持Redis的发布订阅模型

  • 支持Redis哨兵和Redis集群

  • 支持基于Lettuce的响应式编程

  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化

  • 支持基于Redis的JDKCollection实现

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:

快速入门

SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单。

首先,新建一个maven项目,然后按照下面步骤执行:

引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.7</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.heima</groupId><artifactId>redis-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>redis-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><!--redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--common-pool--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!--Jackson依赖--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
配置Redis
spring:data:redis:host: 192.168.200.130port: 6379password: 123456database: 0lettuce:pool:max-active: 8max-idle: 8min-idle: 0max-wait: 1000ms
注入RedisTemplate

因为有了SpringBoot的自动装配,我们可以拿来就用:

	@Resourceprivate RedisTemplate redisTemplate;
编写测试
	@Testvoid testString() {// 设置值redisTemplate.opsForValue().set("name","她说");// 获取值Object name = redisTemplate.opsForValue().get("name");System.out.println("name="+name);}
测试结果

自定义序列化

刚刚的操作,我们是以name为她说进行存储的,但是从下图种可以看出,键和值都变成了一串字符,那么为什么会存在这种情况呢?

这是因为 Spring Data Redis 在默认情况下使用了 Java 序列化机制 来处理对象的存储,导致中文键和值被序列化为了字节流的十六进制表示。

Spring Data Redis 的默认序列化器是 JdkSerializationRedisSerializer,它会:

  • 将对象(包括键和值)通过 Java 自带的序列化机制转换成字节数组

  • 中文在序列化后会变成对应的字节编码(以 \x 开头的十六进制形式)
  • 你看到的 \xAC\xED\x00\x05 是 Java 序列化的魔术头(固定标识),后面的是实际内容的字节表示

这是由SpringDataRedis客户端的默认序列化器 JdkSerializationRedisSerializer造成的,要让我们的键值对显示中文,我们就需要修改其默认的序列化器。

我们可以自定义RedisTemplate的序列化方式,代码如下:

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 {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){// 创建RedisTemplate对象RedisTemplate<String, Object> 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);// 返回return template;}
}

然后,就需要对我们的代码进行修改了:

上面的代码表示,键是String类型的,值是任意的;

除此之外,因为当前项目不是SpringMVC的,所以需要我们主动导入依赖来执行序列化操作,在集成了SpringMVC的项目中,这个依赖是SpringMVC底层自带的:

        <!--Jackson依赖--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency>

然后我们重新运行测试:

从IDEA输出和图形化界面根据可知,键和值一切正常。

再来测试一下,Java对象能不能正常存储,首先创建一个Java对象:

(不知道为何,这里使用Lombok注解总是提示参数个数不一致,所以此处用JavaBean)

public class User {private String name;private Integer age;public User() {}public User(String name, Integer age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public Integer getAge() {return age;}/*** 设置* @param age*/public void setAge(Integer age) {this.age = age;}public String toString() {return "User{name = " + name + ", age = " + age + "}";}
}

启动测试,查看控制台输出:

查看Redis图形化界面根据查看结果:

整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。

StringRedisTemplate

自定义序列化的方式,虽然也能解决Java对象自动序列化为JSON字符串,以及在查询时能够自动把JSON反序列化为Java对象,但是这张方式记录了序列化时对应的class名称,这样带来额外的内存开销。有时候,我们要存储的对象可能还没有class名称所占的内存大,这种情况这样的方式就显得不那么友好了。

为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。

因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。源码如下:

省去了我们自定义RedisTemplate的序列化方式的步骤,而是直接使用:

	@Resourceprivate StringRedisTemplate stringRedisTemplate;// JSON序列化工具private static final ObjectMapper mapper = new ObjectMapper();@Testvoid testString() {// 设置值stringRedisTemplate.opsForValue().set("name","晴天");// 获取值Object name = stringRedisTemplate.opsForValue().get("name");System.out.println("name="+name);}

Redis可视化工具显示为如下图,这说明字符串类型可以直接存储:

当我们需要存储Java对象时,就需要手动序列化和反序列化了,代码如下:

	@Resourceprivate StringRedisTemplate stringRedisTemplate;// JSON序列化工具private static final ObjectMapper mapper = new ObjectMapper();@Testvoid testSaveUser() throws JsonProcessingException {// 创建对象User user = new User("她说", 21);// 手动序列化String json = mapper.writeValueAsString(user);// 写入数据stringRedisTemplate.opsForValue().set("user:200", json);// 获取数据String jsonUser = stringRedisTemplate.opsForValue().get("user:200");// 手动反序列化User user1 = mapper.readValue(jsonUser, User.class);System.out.println("user1 = " + user1);}

可知,这张方式就比自定义序列化多了手动序列化和反序列化的过程,这个过程是由代码来实现的,在Redis上也是以JSON的格式存储的,自然就不会记录序列化时对应的class名称,这种方式也是目前所采用最多的方式,因为其友好性。Redis可视化工具显示如下:

可知,未记录序列化时对应的class名称,看起来清爽不少。

上述两种方式的优缺点如下,没有绝对的好坏,但是第二种更常用,推荐使用:

http://www.xdnf.cn/news/19290.html

相关文章:

  • MyBatis-动态sql
  • 【自记】 Python 中函数参数前加 *(单星号)的解包可迭代对象写法说明
  • 基于三维反投影矫正拼接视频
  • TJA1445学习笔记(二)
  • 咨询进阶——解读 目标管理实务:知识概述、管理概述和实施【附全文阅读】
  • 计算机视觉(四):二值化
  • MySQL面试集合
  • 【C++ 】STL详解(六)—手撸一个属于你的 list!
  • 力扣热题100:合并区间详解(Java实现)(56)
  • 在SAP系统中,如何查询已经被打上了删除标记的生产订单?
  • 数据结构(04)—— 栈和队列
  • [每周一更]-(第158期):构建高性能数据库:MySQL 与 PostgreSQL 系统化问题管理与优化指南
  • 【lua】元表、元方法 详解及应用
  • 【LeetCode_27】移除元素
  • Ubuntu中通过SSH克隆Windows的远程Git仓库(局域网中挺有用)
  • 对于牛客网—语言学习篇—编程初学者入门训练—复合类型:二维数组较简单题目的解析
  • Unity核心概念①
  • 准备机试--图【y总版】[重要]【最短路】
  • 三重积分的对称性
  • shell编程-核心变量知识
  • 面试专栏
  • Agent实战教程:LangGraph结构化输出详解,让智能体返回格式化数据
  • 第N个丑数
  • 文件夹和文件一键加密,保护你的隐私
  • CRM、ERP、HRP系统有啥区别?
  • 本地运行 Ollama 与 DeepSeek R1 1.5B,并结合 Open WebUI 测试
  • 安卓编程 之 线性布局
  • 数组去重【JavaScript】
  • 基于 MyBatis-Plus 拦截器实现锁定特殊数据(二)
  • kmp 算法