Springboot使用RedisTemplate優雅地操作redis(附原始碼)
概述
本文內容主要
-
關於spring-redis
-
關於redis的key設計
-
redis的基本資料結構
-
介紹redis與springboot的整合
-
sringboot中的redistemplate的使用
關於spring-redis
spring-data-redis針對jedis提供瞭如下功能:
1.連線池自動管理,提供了一個高度封裝的“RedisTemplate”類
2.針對jedis客戶端中大量api進行了歸類封裝,將同一型別操作封裝為operation介面
-
ValueOperations:簡單K-V操作
-
SetOperations:set型別資料操作
-
ZSetOperations:zset型別資料操作
-
HashOperations:針對map型別的資料操作
-
ListOperations:針對list型別的資料操作
3.提供了對key的“bound”(繫結)便捷化操作API,可以通過bound封裝指定的key,然後進行一系列的操作而無須“顯式”的再次指定Key,即BoundKeyOperations:
-
BoundValueOperations
-
BoundSetOperations
-
BoundListOperations
-
BoundSetOperations
-
BoundHashOperations
4.將事務操作封裝,有容器控制。
5.針對資料的“序列化/反序列化”,提供了多種可選擇策略(RedisSerializer)
JdkSerializationRedisSerializer:POJO物件的存取場景,使用JDK本身序列化機制,將pojo類通過ObjectInputStream/ObjectOutputStream進行序列化操作,最終redis-server中將儲存位元組序列。是目前最常用的序列化策略。
StringRedisSerializer:Key或者value為字串的場景,根據指定的charset對資料的位元組序列編碼成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封裝。是最輕量級和高效的策略。
JacksonJsonRedisSerializer:jackson-json工具提供了javabean與json之間的轉換能力,可以將pojo例項序列化成json格式儲存在redis中,也可以將json格式的資料轉換成pojo例項。因為jackson工具在序列化和反序列化時,需要明確指定Class型別,因此此策略封裝起來稍微複雜。【需要jackson-mapper-asl工具支援】
OxmSerializer:提供了將javabean與xml之間的轉換能力,目前可用的三方支援包括jaxb,apache-xmlbeans;redis儲存的資料將是xml工具。不過使用此策略,程式設計將會有些難度,而且效率最低;不建議使用。【需要spring-oxm模組的支援】
如果你的資料需要被第三方工具解析,那麼資料應該使用StringRedisSerializer而不是JdkSerializationRedisSerializer。
關於key的設計
key的存活時間:
無論什麼時候,只要有可能就利用key超時的優勢。一個很好的例子就是儲存一些諸如臨時認證key之類的東西。當你去查詢一個授權key時——以OAUTH為例——通常會得到一個超時時間。
這樣在設定key的時候,設成同樣的超時時間,Redis就會自動為你清除。
關係型資料庫的redis
1: 把表名轉換為key字首 如, tag:
2: 第2段放置用於區分割槽key的欄位--對應mysql中的主鍵的列名,如userid
3: 第3段放置主鍵值,如2,3,4…., a , b ,c
4: 第4段,寫要儲存的列名
例:user:userid:9:username
Redis的資料型別
String字串
-
string是redis最基本的型別,一個key對應一個value。
-
string型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。
-
string型別是Redis最基本的資料型別,一個鍵最大能儲存512MB。
String型別的操作參考:
http://www.runoob.com/redis/redis-strings.html
連結串列
-
redis列表是簡單的字串列表,排序為插入的順序。列表的最大長度為2^32-1。
-
redis的列表是使用連結串列實現的,這意味著,即使列表中有上百萬個元素,增加一個元素到列表的頭部或尾部的操作都是在常量的時間完成。
-
可以用列表獲取最新的內容(像帖子,微博等),用ltrim很容易就會獲取最新的內容,並移除舊的內容。
-
用列表可以實現生產者消費者模式,生產者呼叫lpush新增項到列表中,消費者呼叫rpop從列表中提取,如果沒有元素,則輪詢去獲取,或者使用brpop等待生產者新增項到列表中。
List型別的操作參考:
http://www.runoob.com/redis/redis-lists.html
集合
-
redis集合是無序的字串集合,集合中的值是唯一的,無序的。可以對集合執行很多操作,例如,測試元素是否存在,對多個集合執行交集、並集和差集等等。
-
我們通常可以用集合儲存一些無關順序的,表達物件間關係的資料,例如使用者的角色,可以用sismember很容易就判斷使用者是否擁有某個角色。
-
在一些用到隨機值的場合是非常適合的,可以用 srandmember/spop 獲取/彈出一個隨機元素。同時,使用@EnableCaching開啟宣告式快取支援,這樣就可以使用基於註解的快取技術。註解快取是一個對快取使用的抽象,通過在程式碼中新增下面的一些註解,達到快取的效果。
Set型別的操作參考:
http://www.runoob.com/redis/redis-sets.html
ZSet 有序集合
-
有序集合由唯一的,不重複的字串元素組成。有序集合中的每個元素都關聯了一個浮點值,稱為分數。可以把有序看成hash和集合的混合體,分數即為hash的key。
-
有序集合中的元素是按序儲存的,不是請求時才排序的。
ZSet型別的操作型別
http://www.runoob.com/redis/redis-sorted-sets.html
Hash-雜湊
-
redis的雜湊值是字串欄位和字串之間的對映,是表示物件的完美資料型別。
-
雜湊中的欄位數量沒有限制,所以可以在你的應用程式以不同的方式來使用雜湊。
Hash型別的操作參考:
http://www.runoob.com/redis/redis-hashes.html
springboot 與redis的整合
pom檔案
依賴如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<!--springboot配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
application.properties
#Redis資料庫索引(預設為0)
spring.redis.database=0
#Redis伺服器地址
spring.redis.host=127.0.0.1
#Redis伺服器連線埠
spring.redis.port=6379
#Redis伺服器連線密碼(預設為空)
spring.redis.password=
#連線池最大連線數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
#連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
#連線池中的最大空閒連線
spring.redis.pool.max-idle=8
#連線池中的最小空閒連線
spring.redis.pool.min-idle=0
#連線超時時間(毫秒)
spring.redis.timeout=0
redisTemplate的配置
新建一個redisConfig類,進行相關bean的配置:
packagecom.config;
importcom.fasterxml.jackson.annotation.JsonAutoDetect;
importcom.fasterxml.jackson.annotation.PropertyAccessor;
importcom.fasterxml.jackson.databind.ObjectMapper;
importorg.springframework.cache.CacheManager;
importorg.springframework.cache.annotation.CachingConfigurerSupport;
importorg.springframework.cache.annotation.EnableCaching;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.data.redis.cache.RedisCacheManager;
importorg.springframework.data.redis.connection.RedisConnectionFactory;
importorg.springframework.data.redis.core.*;
importorg.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
importorg.springframework.data.redis.serializer.StringRedisSerializer;
/**
*@authorjanti
*reids相關bean的配置
*/
@Configuration
@EnableCaching
publicclassRedisConfigextendsCachingConfigurerSupport{
/**
*選擇redis作為預設快取工具
*@paramredisTemplate
*@return
*/
@Bean
publicCacheManagercacheManager(RedisTemplateredisTemplate){
RedisCacheManagerrcm=newRedisCacheManager(redisTemplate);
returnrcm;
}
/**
*retemplate相關配置
*@paramfactory
*@return
*/
@Bean
publicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactoryfactory){
RedisTemplate<String,Object>template=newRedisTemplate<>();
//配置連線工廠
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值(預設使用JDK的序列化方式)
Jackson2JsonRedisSerializerjacksonSeial=newJackson2JsonRedisSerializer(Object.class);
ObjectMapperom=newObjectMapper();
//指定要序列化的域,field,get和set,以及修飾符範圍,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
//指定序列化輸入的型別,類必須是非final修飾的,final修飾的類,比如String,Integer等會跑出異常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
//值採用json序列化
template.setValueSerializer(jacksonSeial);
//使用StringRedisSerializer來序列化和反序列化redis的key值
template.setKeySerializer(newStringRedisSerializer());
//設定hashkey和value序列化模式
template.setHashKeySerializer(newStringRedisSerializer());
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
returntemplate;
}
/**
*對hash型別的資料操作
*
*@paramredisTemplate
*@return
*/
@Bean
publicHashOperations<String,String,Object>hashOperations(RedisTemplate<String,Object>redisTemplate){
returnredisTemplate.opsForHash();
}
/**
*對redis字串型別資料操作
*
*@paramredisTemplate
*@return
*/
@Bean
publicValueOperations<String,Object>valueOperations(RedisTemplate<String,Object>redisTemplate){
returnredisTemplate.opsForValue();
}
/**
*對連結串列型別的資料操作
*
*@paramredisTemplate
*@return
*/
@Bean
publicListOperations<String,Object>listOperations(RedisTemplate<String,Object>redisTemplate){
returnredisTemplate.opsForList();
}
/**
*對無序集合型別的資料操作
*
*@paramredisTemplate
*@return
*/
@Bean
publicSetOperations<String,Object>setOperations(RedisTemplate<String,Object>redisTemplate){
returnredisTemplate.opsForSet();
}
/**
*對有序集合型別的資料操作
*
*@paramredisTemplate
*@return
*/
@Bean
publicZSetOperations<String,Object>zSetOperations(RedisTemplate<String,Object>redisTemplate){
returnredisTemplate.opsForZSet();
}
}
spring-redis中使用了RedisTemplate來進行redis的操作,通過泛型的K和V設定鍵值對的物件型別。這裡使用了string作為key的物件型別,值為Object。
對於Object,spring-redis預設使用了jdk自帶的序列化,不推薦使用默認了。所以使用了json的序列化方式
對spring-redis對redis的五種資料型別也有支援
HashOperations:對hash型別的資料操作
ValueOperations:對redis字串型別資料操作
ListOperations:對連結串列型別的資料操作
SetOperations:對無序集合型別的資料操作
ZSetOperations:對有序集合型別的資料操作
redis操作的工具類
packagecom.service;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.data.redis.core.RedisTemplate;
importorg.springframework.stereotype.Component;
importjava.util.Collection;
importjava.util.Date;
importjava.util.Set;
importjava.util.concurrent.TimeUnit;
importjava.util.stream.Collectors;
importjava.util.stream.Stream;
@Component
publicclassRedisService{
@Autowired
privateRedisTemplate<String,String>redisTemplate;
/**
*預設過期時長,單位:秒
*/
publicstaticfinallongDEFAULT_EXPIRE=60*60*24;
/**
*不設定過期時長
*/
publicstaticfinallongNOT_EXPIRE=-1;
publicbooleanexistsKey(Stringkey){
returnredisTemplate.hasKey(key);
}
/**
*重名名key,如果newKey已經存在,則newKey的原值被覆蓋
*
*@paramoldKey
*@paramnewKey
*/
publicvoidrenameKey(StringoldKey,StringnewKey){
redisTemplate.rename(oldKey,newKey);
}
/**
*newKey不存在時才重新命名
*
*@paramoldKey
*@paramnewKey
*@return修改成功返回true
*/
publicbooleanrenameKeyNotExist(StringoldKey,StringnewKey){
returnredisTemplate.renameIfAbsent(oldKey,newKey);
}
/**
*刪除key
*
*@paramkey
*/
publicvoiddeleteKey(Stringkey){
redisTemplate.delete(key);
}
/**
*刪除多個key
*
*@paramkeys
*/
publicvoiddeleteKey(String...keys){
Set<String>kSet=Stream.of(keys).map(k->k).collect(Collectors.toSet());
redisTemplate.delete(kSet);
}
/**
*刪除Key的集合
*
*@paramkeys
*/
publicvoiddeleteKey(Collection<String>keys){
Set<String>kSet=keys.stream().map(k->k).collect(Collectors.toSet());
redisTemplate.delete(kSet);
}
/**
*設定key的生命週期
*
*@paramkey
*@paramtime
*@paramtimeUnit
*/
publicvoidexpireKey(Stringkey,longtime,TimeUnittimeUnit){
redisTemplate.expire(key,time,timeUnit);
}
/**
*指定key在指定的日期過期
*
*@paramkey
*@paramdate
*/
publicvoidexpireKeyAt(Stringkey,Datedate){
redisTemplate.expireAt(key,date);
}
/**
*查詢key的生命週期
*
*@paramkey
*@paramtimeUnit
*@return
*/
publiclonggetKeyExpire(Stringkey,TimeUnittimeUnit){
returnredisTemplate.getExpire(key,timeUnit);
}
/**
*將key設定為永久有效
*
*@paramkey
*/
publicvoidpersistKey(Stringkey){
redisTemplate.persist(key);
}
}
redis的key工具類
packagecom.util;
/**
*redisKey設計
*/
publicclassRedisKeyUtil{
/**
*redis的key
*形式為:
*表名:主鍵名:主鍵值:列名
*
*@paramtableName表名
*@parammajorKey主鍵名
*@parammajorKeyValue主鍵值
*@paramcolumn列名
*@return
*/
publicstaticStringgetKeyWithColumn(StringtableName,StringmajorKey,StringmajorKeyValue,Stringcolumn){
StringBufferbuffer=newStringBuffer();
buffer.append(tableName).append(":");
buffer.append(majorKey).append(":");
buffer.append(majorKeyValue).append(":");
buffer.append(column);
returnbuffer.toString();
}
/**
*redis的key
*形式為:
*表名:主鍵名:主鍵值
*
*@paramtableName表名
*@parammajorKey主鍵名
*@parammajorKeyValue主鍵值
*@return
*/
publicstaticStringgetKey(StringtableName,StringmajorKey,StringmajorKeyValue){
StringBufferbuffer=newStringBuffer();
buffer.append(tableName).append(":");
buffer.append(majorKey).append(":");
buffer.append(majorKeyValue).append(":");
returnbuffer.toString();
}
}
如何使用?
測試程式碼
新建一個實體類:
packagecom.domain;
publicclassUserVo{
publicstaticfinalStringTable="t_user";
privateStringname;
privateStringaddress;
privateIntegerage;
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicStringgetAddress(){
returnaddress;
}
publicvoidsetAddress(Stringaddress){
this.address=address;
}
publicIntegergetAge(){
returnage;
}
publicvoidsetAge(Integerage){
this.age=age;
}
@Override
publicStringtoString(){
return"UserVo{"+
"name='"+name+'\''+
",address='"+address+'\''+
",age="+age+
'}';
}
}
再新建一個測試類:
packagecom.config;
importcom.domain.UserVo;
importcom.service.RedisService;
importcom.util.RedisKeyUtil;
importorg.junit.Test;
importorg.junit.runner.RunWith;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.boot.test.context.SpringBootTest;
importorg.springframework.data.redis.core.*;
importorg.springframework.test.context.junit4.SpringRunner;
importjavax.annotation.Resource;
importjava.util.Set;
importjava.util.concurrent.TimeUnit;
importstaticorg.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
publicclassRedisConfigTest{
@Autowired
privateStringRedisTemplatestringRedisTemplate;
@Autowired
privateRedisTemplateredisTemplate;
@Resource
privateValueOperations<String,Object>valueOperations;
@Autowired
privateHashOperations<String,String,Object>hashOperations;
@Autowired
privateListOperations<String,Object>listOperations;
@Autowired
privateSetOperations<String,Object>setOperations;
@Autowired
privateZSetOperations<String,Object>zSetOperations;
@Resource
privateRedisServiceredisService;
@Test
publicvoidtestObj()throwsException{
UserVouserVo=newUserVo();
userVo.setAddress("上海");
userVo.setName("測試dfas");
userVo.setAge(123);
ValueOperations<String,Object>operations=redisTemplate.opsForValue();
redisService.expireKey("name",20,TimeUnit.SECONDS);
Stringkey=RedisKeyUtil.getKey(UserVo.Table,"name",userVo.getName());
UserVovo=(UserVo)operations.get(key);
System.out.println(vo);
}
@Test
publicvoidtestValueOption()throwsException{
UserVouserVo=newUserVo();
userVo.setAddress("上海");
userVo.setName("jantent");
userVo.setAge(23);
valueOperations.set("test",userVo);
System.out.println(valueOperations.get("test"));
}
@Test
publicvoidtestSetOperation()throwsException{
UserVouserVo=newUserVo();
userVo.setAddress("北京");
userVo.setName("jantent");
userVo.setAge(23);
UserVoauserVo=newUserVo();
auserVo.setAddress("n櫃昂周");
auserVo.setName("antent");
auserVo.setAge(23);
setOperations.add("user:test",userVo,auserVo);
Set<Object>result=setOperations.members("user:test");
System.out.println(result);
}
@Test
publicvoidHashOperations()throwsException{
UserVouserVo=newUserVo();
userVo.setAddress("北京");
userVo.setName("jantent");
userVo.setAge(23);
hashOperations.put("hash:user",userVo.hashCode()+"",userVo);
System.out.println(hashOperations.get("hash:user",userVo.hashCode()+""));
}
@Test
publicvoidListOperations()throwsException{
UserVouserVo=newUserVo();
userVo.setAddress("北京");
userVo.setName("jantent");
userVo.setAge(23);
//listOperations.leftPush("list:user",userVo);
//System.out.println(listOperations.leftPop("list:user"));
//pop之後值會消失
System.out.println(listOperations.leftPop("list:user"));
}
}
註解快取的使用
@Cacheable:在方法執行前Spring先檢視快取中是否有資料,如果有資料,則直接返回快取資料;沒有則呼叫方法並將方法返回值放進快取。
@CachePut:將方法的返回值放到快取中。
@CacheEvict:刪除快取中的資料。
本文程式碼
https://github.com/JayTange/springbootRedis.git
參考
https://www.cnblogs.com/chiangchou/p/7748009.html
http://www.cnblogs.com/chiangchou/p/redis-1.html
寫在最後
分享一些Java學習資料獲取方式:點選連結《Java面試BAT通關手冊》,覆蓋了Java核心技術、JVM、Java併發、SSM、微服務、資料庫、資料結構等等。