在Redis官网中提供了各种语言的客户端,地址:https://redis.io/docs/clients/
其中也包含很多Java客户端:
常用的Java客户端有Jedis、Lettuce和Redisson
Jedis的官网地址: https://github.com/redis/jedis
1)创建一个普通的maven项目,引入依赖:
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.7.0version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>5.7.0version>
<scope>testscope>
dependency>
2)新建一个单元测试类,内容如下:
/**
* Jedis测试类
*/
public class JedisTest {
private Jedis jedis;
/**
* 建立Redis连接,初始化jedis
*/
@BeforeEach
public void setUp(){
// 1.建立连接,参数需要填自己虚拟机的地址与Redis的端口号
jedis = new Jedis("192.168.211.100", 6379);
// 2.Redis连接密码
jedis.auth("123456");
// 3.选择使用Redis中的哪个库
jedis.select(0);
}
/**
* 测试string类型的基本使用
*/
@Test
public void testString(){
jedis.set("name", "张三");
String name = jedis.get("name");
System.out.println(name);
}
/**
* 测试hash类型的基本使用
*/
@Test
public void testHash(){
jedis.hset("user","name","李四");
String name = jedis.hget("user", "name");
System.out.println(name);
}
/**
* 连接使用之后需要被释放
*/
@AfterEach
public void tearDown(){
if(jedis != null){
jedis.close();
}
}
}
可以看到,jedis的API与redis指令是比较相似的。
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐大家使用Jedis连接池代替Jedis的直连方式。
1)新建一个包,名为com.jedis.util,并在包中创建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.211.100", 6379, 1000, "123456");
}
/**
* 设置静态方法,用于返回一个jedis连接
* @return
*/
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
2)将测试类中建立redis连接的方法进行修改,其他代码不变:
/**
* 建立Redis连接,初始化jedis
*/
@BeforeEach
public void setUp(){
// 1.建立连接,参数需要填自己虚拟机的地址与Redis的端口号
// jedis = new Jedis("192.168.211.100", 6379);
// 1.通过jedis连接工厂获取jedis连接
jedis = JedisConnectionFactory.getJedis();
// 2.选择使用Redis中的哪个库
jedis.select(0);
}
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis,SpringData有如下特点:
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将针对不同数据类型的操作API封装到了不同的数据类型中,以便区别:
在实际开发中,我们一般都会使用SpringDataRedis,而非jedis
SpringBoot已经提供了对SpringDataRedis的支持,使用起来非常简单。
1)新建一个SpringBoot项目,并引入以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
2)在application.yml中添加以下配置
spring:
redis:
# 配置连接信息
host: 192.168.211.100
port: 6379
password: 123456
# 配置连接池信息
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100ms
3)在测试类中编写测试代码
@SpringBootTest
class RedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testString(){
//写入一条string类型的数据
redisTemplate.opsForValue().set("name","张三");
//获取一条string类型的数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
}
}
当我们在执行上述代码之后,点开redis图形界面客户端,发现插入的string类型的数据变成了这样:
这显然与我们插入的键值对不符合,但是当我们获取键为name的值并打印之后,发现结果又是正常的
这又是为什么呢?
不知道各位小伙伴有没有注意到,当我们在使用redisTemplate操作string数据类型时,其api允许我们使用object类型的对象作为key或者value
但是我们知道,string类型的key和value在redis当中应该都是字符串才对,那为什么redisTemplate允许我们使用object类型呢?其底层又做了什么样的处理呢?
其实redisTemplate将Object写入redis之前,会先将object序列化为字节形式,默认是采用JDK序列化,而JDK序列化之后得到的结果就是我们刚刚看到的这个样子
这种序列化方式的缺点是很明显的,比如内存占用大,可读性差等等。
redisTemplate允许我们对序列化方式进行自定义,并提供了几种序列化器供我们使用,常见的序列化器有以下几种
常用的是 StringRedisSerializer 和 GenericJackson2JsonRedisSerializer , StringRedisSerializer是专门用来处理String类型,将String类型转为字节数组的序列化器,而GenericJackson2JsonRedisSerializer 是将对象转为json串的的序列化器
我们可以自定义RedisTemplate的序列化方式,代码如下:
@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的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}
再执行以下代码:
@Test
public void testString(){
//写入一条string类型的数据
redisTemplate.opsForValue().set("name","张三");
//获取一条string类型的数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
}
结果正常,这样才是我们想要看到的样子。
如果我们插入的是一个对象而不是字符串,那么又能否插入成功呢?来测试一下:
@Test
public void testStringObject(){
//写入一条string类型的数据,值类型为Date
redisTemplate.opsForValue().set("birthday",new Date());
Object birthday = redisTemplate.opsForValue().get("birthday");
System.out.println(birthday);
}
打印结果正常
打开图形化客户端再看看
结果也是正常的,因为我们使用了JSON序列化来代替默认的JDK序列化方式,整体可读性有了很大提升。我们还发现保存的json串中还带有该json的类型,这是因为无论是java对象的序列化还是反序列化,都是redisTemplate帮我们自动完成的,因此redisTemplate在对对象进行序列化时必须将该java对象的类型一并保存下来,但是这样的话就带来了额外的内存开销,针对这种问题有没有解决方案呢?
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。如果我们需要存储Java对象,就手动对java对象进行序列化或反序列化处理
因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。
那我们现在变更了序列化器,是否需要重新定义一下RedisTemplate的序列化规则呢?答案是不用
由于这种用法比较普遍,所以SpringDataRedis提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
演示如下,这里的序列化方式使用的是jackson
// 注入StringRedisTemplate
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 定义JSON序列化工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
public void testStringRedisTemplate() throws JsonProcessingException {
//手动序列化
String now = mapper.writeValueAsString(new Date());
stringRedisTemplate.opsForValue().set("now",now);
String result = stringRedisTemplate.opsForValue().get("now");
//手动反序列化
Date date = mapper.readValue(result, Date.class);
System.out.println(date);
}
再次查看图形化客户端,此时插入的数据中已经没有数据类型的相关信息了