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 異常,解決辦法總結