spring boot 開發—第五篇SpringDataJPA整合 Redis 實現快取
阿新 • • 發佈:2019-02-07
對druid專案進行改造,增加redis快取支援。
1、redis簡介
Redis 是完全開源免費的,遵守BSD協議,是一個高效能的key-value資料庫。
Redis 與其他 key - value 快取產品有以下三個特點:
- Redis支援資料的持久化,可以將記憶體中的資料儲存在磁碟中,重啟的時候可以再次載入進行使用。
- Redis不僅僅支援簡單的key-value型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。
- Redis支援資料的備份,即master-slave模式的資料備份。
2、Redis 優勢
- 效能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
- 豐富的資料型別 – Redis支援二進位制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 資料型別操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支援事務,即原子性,通過MULTI和EXEC指令包起來。
- 豐富的特性 – Redis還支援 publish/subscribe, 通知, key 過期等等特性。
3、構建專案
修改pom檔案,增加對redis的依賴。pom檔案如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.vesus</groupId> <artifactId>springboot-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-redis</name> <description>Demo project for Spring Boot</description> <parent> <groupId>com.vesus</groupId> <artifactId>spring-boot-demo</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring jpa--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--引入druid最新maven依賴--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.29</version> </dependency> <!--引入redis的支援--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、配置application.properties
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=123456 spring.jpa.show-sql=true spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy spring.jpa.database=mysql ########################## druid配置 ########################## spring.datasource.type=com.alibaba.druid.pool.DruidDataSource #最大活躍數 spring.datasource.maxActive=20 #初始化數量 spring.datasource.initialSize=5 #最小活躍數 spring.datasource.minIdle=5 #配置超市等待時間 spring.datasource.maxWait=60000 #配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 spring.datasource.timeBetweenEvictionRunsMillis=60000 #配置一個連線在池中最小生存的時間,單位是毫秒 spring.datasource.minEvictableIdleTimeMillis=300000 spring.datasource.validationQuery=SELECT 1 FROM t_user spring.datasource.testWhileIdle=true spring.datasource.testOnBorrow=false spring.datasource.exceptionSorter=true spring.datasource.testOnReturn=false #開啟PSCache,並且指定每個連線上PSCache的大小 spring.datasource.poolPreparedStatements=true spring.datasource.maxPoolPreparedStatementPerConnectionSize=20 #配置監控統計攔截的filters,去掉後監控介面sql無法統計,'wall'用於防火牆 spring.datasource.filters=stat,wall,log4j #通過connectProperties屬性來開啟mergeSql功能;慢SQL記錄 spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #合併多個DruidDataSource的監控資料 spring.datasource.useGlobalDataSourceStat=true ###########################redis######################### #Redis資料庫索引(預設為0) spring.redis.database=0 #Redis伺服器地址 spring.redis.host=10.211.55.5 #Redis伺服器連線埠 spring.redis.port=6379 #Redis伺服器連線密碼(預設為空) spring.redis.password= #連線池最大連線數(使用負值表示沒有限制) spring.redis.pool.max-active=10 #連線池最大阻塞等待時間(使用負值表示沒有限制) spring.redis.pool.max-wait=-1 #連線池中的最大空閒連線 spring.redis.pool.max-idle=8 #連線池中的最小空閒連線 spring.redis.pool.min-idle=0 #連線超時時間(毫秒) spring.redis.timeout=0
3、編寫RedisConfig
3.1 模板檔案
Spring Data Redis提供了兩個模板:
RedisTemplate
StringRedisTemplate
首先我們先建立一個RedisTemplate模板類,型別的key是String型別,value是Object型別(如果key和value都是String型別,建議使用StringRedisTemplate)
3.2、序列化
當儲存一條資料的時候,key和value都要被序列化成json資料,取出來的時候被序列化成物件,key和value都會使用序列化器進行序列化,spring data redis提供多個序列化器
- GenericToStringSerializer:使用Spring轉換服務進行序列化;
- JacksonJsonRedisSerializer:使用Jackson 1,將物件序列化為JSON;
- Jackson2JsonRedisSerializer:使用Jackson 2,將物件序列化為JSON;
- JdkSerializationRedisSerializer:使用Java序列化;
- OxmSerializer:使用Spring O/X對映的編排器和解排器(marshaler和unmarshaler)實
- 現序列化,用於XML序列化;
- StringRedisSerializer:序列化String型別的key和value。
RedisTemplate會預設使用JdkSerializationRedisSerializer,這意味著key和value都會通過Java進行序列化。StringRedisTemplate預設會使用StringRedisSerializer
3.3、 例項程式碼如下:
package com.vesus.springbootredis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.lang.reflect.Method;
//Redis 快取配置類
public class RedisConfig extends CachingConfigurerSupport {
//快取物件集合中,快取是以key-value的形式儲存的,當不指定快取key是,會使用KeyGenerator生成key
//定義快取資料 key 生成策略的bean包名+類名+方法名+所有引數
@Bean
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer buffer = new StringBuffer();
buffer.append(target.getClass().getName());
buffer.append(method.getName());
for (Object obj : params) {
buffer.append(obj.toString());
}
return buffer.toString();
}
};
};
//要啟用spring快取支援,需建立一個 CacheManager的 bean,CacheManager 介面有很多實現,
// 這裡Redis 的整合,用 RedisCacheManager這個實現類
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate){
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
return cacheManager ;
}
//redis模板,Spring Data Redis提供了兩個模板:RedisTemplate,StringRedisTemplate
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
StringRedisTemplate template = new StringRedisTemplate(factory);
//序列化
//spring data redis提供多個序列化器
//GenericToStringSerializer:使用Spring轉換服務進行序列化;
//JacksonJsonRedisSerializer:使用Jackson 1,將物件序列化為JSON;
//Jackson2JsonRedisSerializer:使用Jackson 2,將物件序列化為JSON;
//JdkSerializationRedisSerializer:使用Java序列化;
//OxmSerializer:使用Spring O/X對映的編排器和解排器(marshaler和unmarshaler)實
//現序列化,用於XML序列化;
//StringRedisSerializer:序列化String型別的key和value。
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
template.setValueSerializer(serializer);
template.afterPropertiesSet();
return template ;
}
}
4、編寫Impl
4.1、Cache註解詳解
- @CacheConfig:主要用於配置該類中會用到的一些共用的快取配置。在這裡@CacheConfig(cacheNames = “users”):配置了該資料訪問物件中返回的內容將儲存於名為users的快取物件中,我們也可以不使用該註解,直接通過@Cacheable自己配置快取集的名字來定義。
- @Cacheable:配置了findByName函式的返回值將被加入快取。同時在查詢時,會先從快取中獲取,若不存在才再發起對資料庫的訪問。該註解主要有下面幾個引數:
- value、cacheNames:兩個等同的引數(cacheNames為Spring 4新增,作為value的別名),用於指定快取儲存的集合名。由於Spring 4中新增了@CacheConfig,因此在Spring 3中原本必須有的value屬性,也成為非必需項了
- key:快取物件儲存在Map集合中的key值,非必需,預設按照函式的所有引數組合作為key值,若自己配置需使用SpEL表示式,比如:@Cacheable(key = “#p0”):使用函式第一個引數作為快取的key值.
- condition:快取物件的條件,非必需,也需使用SpEL表示式,只有滿足表示式條件的內容才會被快取,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有當第一個引數的長度小於3的時候才會被快取,若做此配置上面的AAA使用者就不會被快取,讀者可自行實驗嘗試。
- unless:另外一個快取條件引數,非必需,需使用SpEL表示式。它不同於condition引數的地方在於它的判斷時機,該條件是在函式被呼叫之後才做判斷的,所以它可以通過對result進行判斷。
- keyGenerator:用於指定key生成器,非必需。若需要指定一個自定義的key生成器,我們需要去實現org.springframework.cache.interceptor.KeyGenerator介面,並使用該引數來指定。需要注意的是:該引數與key是互斥的
- cacheManager:用於指定使用哪個快取管理器,非必需。只有當有多個時才需要使用
- cacheResolver:用於指定使用那個快取解析器,非必需。需通過org.springframework.cache.interceptor.CacheResolver介面來實現自己的快取解析器,並用該引數指定。
除了這裡用到的兩個註解之外,還有下面幾個核心註解:
- @CachePut:配置於函式上,能夠根據引數定義條件來進行快取,它與@Cacheable不同的是,它每次都會真是呼叫函式,所以主要用於資料新增和修改操作上。它的引數與@Cacheable類似,具體功能可參考上面對@Cacheable引數的解析
- @CacheEvict
:配置於函式上,通常用在刪除方法上,用來從快取中移除相應資料。除了同
@Cacheable
一樣的引數之外,它還有下面兩個引數:
- allEntries:非必需,預設為false。當為true時,會移除所有資料
- beforeInvocation:非必需,預設為false,會在呼叫方法之後移除資料。當為true時,會在呼叫方法之前移除資料。
4.2、例項程式碼
package com.vesus.springbootredis.service.impl;
import com.vesus.springbootredis.model.User;
import com.vesus.springbootredis.repository.UserRepository;
import com.vesus.springbootredis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserRepository userRepository ;
//@Cacheable,如果沒有指定key則方法引數作為key儲存到快取中。
@Cacheable(value = "users")
public List<User> findAll() {
return userRepository.findAll();
}
//@CachePut快取新增的或更新的資料到快取,其中快取名字是 user 。資料的key是user的id
@CachePut(value = "user",key = "#user.id")
public void saveUser(User user) {
userRepository.save(user);
}
@Cacheable(value = "user",key = "#id")
public User findOne(long id) {
User user = userRepository.findOne(id);
return user ;
}
//@CacheEvict 從快取user中刪除key為id 的資料
@CacheEvict(value = "user",key = "#id")
public void delete(long id) {
}
@Override
public List<User> findByName(String name) {
return null;
}
}
5、編寫controller
package com.vesus.springbootredis.controller;
import com.vesus.springbootredis.model.User;
import com.vesus.springbootredis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
UserService userService ;
@RequestMapping(value = "/userlist")
public List<User> getUserList(){
return userService.findAll() ;
}
@RequestMapping(value = "/save")
public void saveUser(){
User user = new User() ;
user.setId(2);
user.setName("user2");
user.setAge(22);
userService.saveUser(user);
}
@RequestMapping(value = "/findbyid")
public User findById(){
return userService.findOne(new Long(1));
}
@RequestMapping(value = "/delid")
public void delid(){
userService.delete(new Long(1));
}
}
第一次查詢資料庫
Hibernate: select user0_.t_id as t_id1_0_0_, user0_.t_address as t_addres2_0_0_, user0_.t_age as t_age3_0_0_, user0_.t_name as t_name4_0_0_, user0_.t_pwd as t_pwd5_0_0_ from t_user user0_ where user0_.t_id=?
再次重新整理,不訪問資料庫