Spring操作Redis数据库,RedisTemplate和StringRedisTemplate差异

Redis是KV内存数据库,支持很多数据结构,具体如下面之前写过的,也可以自行google

http://lihuia.com/?s=redis

Spring对Redis操作的支持,基本也和JDBC,MongoDB类似,通过Spring Data Redis,具体有

1、客户端支持,Jedis和Lettuce

2、RedisTemplate

3、Repository支持

测试一下通过Jedis来操作数据库,而Jedis非线程安全,所以一般通过JedisPool线程安全的连接池创建一些Jedis实例,要用的时候就从池子里拿Jedis实例来进行redis操作,用完后归还给JedisPool

可以简单看下JedisPool,它继承了JedisPoolAbstract

public class JedisPool extends JedisPoolAbstract {

JedisPoolAbstract继承了泛型Pool

public class JedisPoolAbstract extends Pool<Jedis> {

具体类的定义里

public abstract class Pool<T> implements Closeable {

从下面可以看出来最终调用getResource()方法,就可以从池子里获取一个Jedis对象,进而对redis进行各种操作

public T getResource() {
try {
return internalPool.borrowObject();
} catch (NoSuchElementException nse) {
if (null == nse.getCause()) { // The exception was caused by an exhausted pool
throw new JedisExhaustedPoolException(
"Could not get a resource since the pool is exhausted", nse);
}
// Otherwise, the exception was caused by the implemented activateObject() or ValidateObject()
throw new JedisException("Could not get a resource from the pool", nse);
} catch (Exception e) {
throw new JedisConnectionException("Could not get a resource from the pool", e);
}
}

因此,要想操作Redis,只需要配置好JedisPool的Bean就行了

具体配置类如下,参考JedisPool的构造方法即可配置

package com.lihuia.redis.jedis;

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
* Copyright (C), 2018-2019
* FileName: JedisConfig
* Author: lihui
* Date: 2019/11/12
*/

@PropertySource(value = {"classpath:config/redis.properties"})
@Configuration
public class JedisConfig {

@Bean
public JedisPoolConfig jedisPoolConfig() {
return new JedisPoolConfig();
}

@Bean(destroyMethod = "close")
public JedisPool jedisPool(@Value("${redis.host}") String host) {
return new JedisPool(jedisPoolConfig(), host);
}
}

这里redis连接配置在redis.properties里,通过@PropertySource注解指明,内容如下

redis.host=localhost

同时Bean添加一个属性方法,在关闭这个Bean之前,close关闭连接

而JedisPoolConfig里配置了一些池子的基本属性

package redis.clients.jedis;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
// defaults to make your life with connection pool easier :)
setTestWhileIdle(true);
setMinEvictableIdleTimeMillis(60000);
setTimeBetweenEvictionRunsMillis(30000);
setNumTestsPerEvictionRun(-1);
}
}

有了这些之后,先本地起一个redis-server,然后以最简单的String类型为例,其他数据类型也一样

127.0.0.1:6379> set one 1
OK
127.0.0.1:6379> get one
"1"
127.0.0.1:6379>

测试类就比较容易了

package com.lihuia.redis.jedis;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: NumTest
* Author: lihui
* Date: 2019/11/12
*/

@ContextConfiguration(classes = {JedisConfig.class})
@Slf4j
public class JedisTest extends AbstractTestNGSpringContextTests {

@Resource
private JedisPool jedisPool;

@Test(description = "字符串类型")
public void stringTest() {
try (Jedis jedis = jedisPool.getResource()) {
log.info("String: one => " + jedis.get("one"));
}
}
}

执行结果如下

[main] INFO com.lihuia.redis.jedis.JedisTest - String: one => 1

 

再来试试RedisTemplate的操作方法,和之前JdbcTemplate和MongoTemplate一样,关键的是Bean的配置,配置类如下

package com.lihuia.redis.template;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

/**
* Copyright (C), 2018-2019
* FileName: RedisConfig
* Author: lihui
* Date: 2019/11/12
*/

@PropertySource(value = {"classpath:config/redis.properties"})
@Configuration
public class RedisConfig {

@Value("${redis.host}")
private String hostname;

@Bean
public RedisStandaloneConfiguration redisStandaloneConfiguration() {
return new RedisStandaloneConfiguration(hostname);
}

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new JedisConnectionFactory(redisStandaloneConfiguration());
}

@Bean
public RedisTemplate redisTemplate() {
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}

这里配置RedisTemplate类的时候,并没有指明泛型KV的类型,可以进行操作的时候再进行类型转换

测试类如下,基本上每种数据类型的操作都可以他通过RedisTemplate.opsForXXX来完成

package com.lihuia.redis.template;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: NumTest
* Author: lihui
* Date: 2019/11/12
*/

@ContextConfiguration(classes = {RedisConfig.class})
@Slf4j
public class TemplateTest extends AbstractTestNGSpringContextTests {

@Resource
private RedisTemplate redisTemplate;

@Test(description = "字符串校验")
public void stringTest() {
log.info((String) redisTemplate.opsForValue().get("one"));
}
}

但是,这里的测试执行结果是null

[main] INFO com.lihuia.redis.template.TemplateTest - null

原因是对于RedisTemplate,存和取都是将数据序列化为字节数组后,存到Redis,而读取也必须是读取字节数组,而显然我刚通过redis-cli写入的{“one”:”1”}是完全可以读取的,因此通过RedisTemplate是无法读取的

想证明这一点很容易,通过调用redisTemplate.opsForValue().set()方法插入数据,这肯定是序列化了的,再get看结果

package com.lihuia.redis.template;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: NumTest
* Author: lihui
* Date: 2019/11/12
*/

@ContextConfiguration(classes = {RedisConfig.class})
@Slf4j
public class TemplateTest extends AbstractTestNGSpringContextTests {

@Resource
private RedisTemplate redisTemplate;

@Test(description = "字符串校验")
public void stringTest() {
redisTemplate.opsForValue().set("three", "3");
log.info((String) redisTemplate.opsForValue().get("three"));
}
}

测试的结果,这样就对了

[main] INFO com.lihuia.redis.template.TemplateTest - 3

而对于字符串类型,有一个专门的stringRedisTemplate来操作,十分方便,Bean如下

@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
return stringRedisTemplate;
}

然后对于可见的{“one”:”1”}也是可以正常获取的

package com.lihuia.redis.template;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: NumTest
* Author: lihui
* Date: 2019/11/12
*/

@ContextConfiguration(classes = {RedisConfig.class})
@Slf4j
public class TemplateTest extends AbstractTestNGSpringContextTests {

@Resource
private RedisTemplate redisTemplate;

@Resource
private StringRedisTemplate stringRedisTemplate;

@Test(description = "字符串校验")
public void stringTest() {
log.info(stringRedisTemplate.opsForValue().get("one"));
}
}

RedisTemplate和StringRedisTemplate他们两的序列化类型不太一样

RedisTemplate使用JdkSerializationRedisSerializer序列化对象
StringRedisTemplate使用StringRedisSerializer序列化

因此,对于操作redis数据:

1、各自只能管理各自操作的数据,比如RedisTemplate写的,就不能用StringRedisTemplate来读取

2、如果处理的是Redis里的String类型,那就直接用StringRedisTemplate操作即可

3、如果处理的是Redis里的其它对象,可以用RedisTemplate来操作

4、如果通过客户端可以读取,或者获取的是NULL,那么可以用StringRedisTemplate来操作

 

最后测试一下操作对象

定义一个User类,序列化不要丢了

package com.lihuia.redis.template;

import java.io.Serializable;

import lombok.Builder;

/**
* Copyright (C), 2018-2019
* FileName: Num
* Author: lihui
* Date: 2019/11/12
*/

@Builder
public class User implements Serializable {

private static final long serialVersionUID = 2182129585108022309L;

private int age;
private String phone;

@Override
public String toString() {
return "age=" + age + ", phone=" + phone;
}
}

测试类,泛型RedisTemplate<String, User>来操作

package com.lihuia.redis.template;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: NumTest
* Author: lihui
* Date: 2019/11/12
*/

@ContextConfiguration(classes = {RedisConfig.class})
@Slf4j
public class TemplateTest extends AbstractTestNGSpringContextTests {

@Resource
private RedisTemplate<String, User> redisTemplate;

@Test(description = "对象校验")
public void redisTest() {
redisTemplate.opsForValue().set("lihui", User.builder().age(18).phone("17777777777").build());
redisTemplate.opsForValue().set("lilei", User.builder().age(19).phone("18888888888").build());
log.info(redisTemplate.opsForValue().get("lihui").toString());
log.info(redisTemplate.opsForValue().get("lilei").toString());
}
}

测试结果如下

[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection.
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection.
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection.
[main] INFO com.lihuia.redis.template.TemplateTest - age=18, phone=17777777777
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
[main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection.
[main] INFO com.lihuia.redis.template.TemplateTest - age=19, phone=18888888888

 

Repository相关的就不试了,OVER

发表评论