Spring Boot整合Redis有两种方式,分别是Jedis和RedisTemplate,那么它们二者有什么区别呢?
1、Jedis是Redis官方推荐的面向Java操作Redis的客户端,可以通过JedisPool来获取Jedis连接进行set、get、del等操作,相对简单。RedisTemplate是SpringDataRedis中对JedisAPI的高度封装。
2、RedisTemplate相对于Jedis来说更方便更换Redis客户端,比Jedis多了自动管理连接池的特性。
spring boot使用jedis整合redis
1、添加依赖
<!--Redis客户端: jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.3</version>
</dependency>
2、application.yml配置Redis配置属性
#Redis配置属性
redis:
#主机IP
host: 136.50.201.130
#Redis默认端口号
port: 6379
#Redis服务器连接密码(默认为空)
password:
#Redis数据库索引(默认为0),redis默认提供16个数据库(0-15)
database: 0
#jedis连接池
pool:
#连接池中允许的最大连接数(默认值为8):即能同时建立的‘最大连接个数’,连接全部用完,则需进行等待(使用负值表示没有限制)
max-active: 1024
#允许池中空闲的最小连接数:低于minIdle时,将创建新的连接
min-idle: 0
#连接池中最多可空闲maxIdle个连接(默认值为8) ,这里取值为100,表示即使没有数据库连接时依然可以保持100空闲的连接,而不被清除,随时处于待命状态
max-idle: 100
#等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
max-wait: 15000
#连接超时时间(毫秒)
timeout: 5000
3、读取yml文件中redis的配置属性,注入JedisPoolConfig对象,然后根据JedisPoolConfig和redis相关配置属性创建JedisPool连接池,再通过getResource()从JedisPool连接池获取jedis连接对象。
package com.cd.o2o.cache;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration //标注@Configuration的类,相当于一个xml配置文件
public class JedisPoolUtil {
//redis配置属性读取
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
@Value("${redis.database}")
private int database;
@Value("${redis.pool.max-active}")
private int maxActive;
@Value("${redis.pool.max-idle}")
private int maxIdle;
@Value("${redis.pool.min-idle}")
private int minIdle;
@Value("${redis.pool.max-wait}")
private long maxWaitMillis;
@Autowired
private JedisPoolConfig jedisPoolConfig;
@Autowired
private JedisPool jedisPool;
@Bean //标注@Bean后表明返回对象会被当做一个Bean注册到Spring IoC容器
public JedisPoolConfig createJedisPoolConfig(){
JedisPoolConfig jedisPoolConfig = null;
try{
//创建连接池配置类
jedisPoolConfig = new JedisPoolConfig();
//控制连接池最多可以分配多少个jedis实例
jedisPoolConfig.setMaxTotal(maxActive);
//连接池最大空闲连接数
jedisPoolConfig.setMaxIdle(maxIdle);
//连接池最小空闲连接数,小于这个数会自动创建连接
jedisPoolConfig.setMinIdle(minIdle);
//连接池最大阻塞时间
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
}catch (Exception e){
e.printStackTrace();
}
return jedisPoolConfig;
}
@Bean
public JedisPool createJedisPool(){
JedisPool jedisPool = null;
try{
//创建jedisPool连接池
jedisPool = new JedisPool(jedisPoolConfig,host,port,database);
}catch (Exception e){
e.printStackTrace();
}
return jedisPool;
}
@Bean
public Jedis getJedis(){
//获取jedis连接对象
return jedisPool.getResource();
}
}
4、编写redis工具类,通过jedis连接对象获取对key进行操作,这里我仅提供string存储结构的操作方法。(调用jedis连接对象后,记得释放连接)
package com.cd.o2o.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.util.SafeEncoder;
import java.util.Set;
@Component //标注@Component,表示将该类注册到Spring IoC容器
public class JedisUtil {
@Autowired
private Jedis jedis;
//操作key的方法 (用于判断某个key是否存在,需不需要清空key里的数据),这是对key的操作
/**
* 判断某个Key是否存在
*
* @param key
* @return boolean
*/
public boolean exists(String key){
boolean exists = false;
try{
//调用jedis的exists()方法,判断某个Key是否存在于Redis服务器中
exists = jedis.exists(key);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return exists;
}
/**
* 查找所有给定模式的key
*
* @param pattern key表达式:*表示多个 ?表示一个 (粒如:shop*表示所有以shop开头的key)
* @return
*/
public Set<String> keys(String pattern){
Set<String> set = null;
try{
//调用jedis的keys()方法,获取匹配的key,并保存到Set集合中
set = jedis.keys(pattern);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return set;
}
/**
* 删除key对应的记录,可以是多个记录
*
* @param keys
* @return 删除的记录数
*/
public long del(String... keys){
long count = 0;
try{
//调用jedis的del()方法将相关的Keys删除,并返回删除的记录数
count = jedis.del(keys);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return count;
}
/**
* 清除所有的key
* @return 状态码
*/
public String flushAll(){
String state = null;
try{
//调用jedis的flushAll()方法,清空所有的key
state = jedis.flushAll();
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return state;
}
/**
* 修改key名,如果新key已存在,则旧key的值会覆盖新key的值
*
* @param oldKey
* @param newKey
* @return 状态码
*/
public String rename(String oldKey,String newKey){
String state = null;
try{
state = jedis.rename(SafeEncoder.encode(oldKey),SafeEncoder.encode(newKey));
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return state;
}
/**
* 在新key不存在时,将旧key修改成该新key的名称
*
* @param oldKey
* @param newKey
* @return 状态码
*/
public Long renamenx(String oldKey,String newKey){
long status = 0;
try{
//重命名key
status = jedis.renamenx(oldKey,newKey);
}catch (Exception e){
//关闭jedis连接,避免资源占用
jedis.close();
}
return status;
}
//对存储结构为String的操作(Redis中,字符串类型的value最大可容纳的数据为512M),这是对value的操作
/**
* 根据key名,获取其存储数据
* @param key
* @return
*/
public String get(String key){
String value = null;
try{
//获取指定key的存储数据
value = jedis.get(key);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return value;
}
/**
* 添加记录,如果记录已存在则覆盖原有的value
* @param key
* @param value
* @return
*/
public String set(String key,String value){
String state = null;
try{
//设置指定key的存储数据
state = jedis.set(key,value);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭jedis连接,避免资源占用
jedis.close();
}
return state;
}
}
ok,相关操作已完成
实际项目开发中,我们会先从redis中获取指定数据,如果redis中没有该数据,那么我们再从mysql数据库中取出该数据。并且为了下次能直接从redis中取出该数据,需要将从mysql中取出的数据添加到redis中。由于前面我指定了string作为redis的存储结构,所以需要将从mysql中取出的数据通过json解析工具解析成string,然后再添加进redis中。
package com.cd.o2o.service.impl;
import com.cd.o2o.cache.JedisUtil;
import com.cd.o2o.dao.AreaDao;
import com.cd.o2o.entity.Area;
import com.cd.o2o.service.AreaService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.List;
@Service("areaService")
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaDao areaDao;
@Autowired
private JedisUtil jedisUtil;
//指定key前缀
private static final String AREALIST = "arealist";
@Override
@Transactional(propagation = Propagation.REQUIRED)
public List<Area> getAreaList() {
//定义Redis的key名
String key = AREALIST;
//定义接收对象
List<Area> areaList = null;
//定义Jackson数据转换操作类
ObjectMapper objectMapper = new ObjectMapper();
//判断key是否存在
if(!jedisUtil.exists(key)){
//若key不存在,则从数据库中取出相应数据,并添加进redis里
areaList = areaDao.queryArea();
String jsonString = null;
try {
//将从数据库中取出的实体类集合类型数据转化成String类型数据
jsonString = objectMapper.writeValueAsString(areaList);
} catch (JsonProcessingException e) {
e.printStackTrace();
//抛出RuntimeException才能回滚事务
throw new RuntimeException(e.getMessage());
}
//将转化后的区域信息设置进指定的key中
jedisUtil.set(key,jsonString);
}else {
//若key存在,则从redis中取出该key对应的数据
String jsonString = jedisUtil.get(key);
//指定要将string转化成的复杂集合类型 List<Area>
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class,Area.class);
try {
//将json字符串转化成List<Area>对象
areaList = objectMapper.readValue(jsonString,javaType);
} catch (IOException e) {
e.printStackTrace();
//抛出RuntimeException才能回滚事务
throw new RuntimeException(e.getMessage());
}
}
return areaList;
}
}
总结
- 添加jedis依赖
- 在yml文件中配置redis的相关配置属性
- 读取yml中redis的配置属性,注入JedisPoolConfig对象,然后根据JedisPoolConfig及相关属性配置JedisPool连接池,再通过getResource()方法从JedisPool连接池中获取jedis连接对象
- 调用Jedis连接对象进行set、get、del等操作
- 如果redis缓存中有指定数据,则从缓存中获取,否则从mysql数据库中获取,再添加进redis缓存中
redis使用中经常出现 Could not get a resource from the pool 异常,解决办法总结