springboot 2.X+redis+springcache 之 資料快取
阿新 • • 發佈:2019-02-18
Redis簡介
Redis支援資料的持久化,可以將記憶體中的資料儲存在磁碟中,重啟的時候可以再次載入進行使用。
Redis不僅僅支援簡單的key-value型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。
Redis支援資料的備份,即master-slave模式的資料備份。
第一步:
pom.xml檔案:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId >
</dependency>
第二步:
application.properties檔案:
spring.redis.database=0
# redis server host
spring.redis.host=127.0.0.1
# redis password
spring.redis.password=
# connection port
spring.redis.port=6379
# time out
spring.redis.timeout=10000
# 配置快取過期時間
spring.redis.expiretime=1800
# pool settings:
# 連線池中的最大空閒連線
spring.redis.pool.max-idle=8
# 連線池中的最小空閒連線
spring.redis.pool.min-idle=0
# 連線池最大連線數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# set Key prefix.
# spring.cache.redis.key-prefix=dev
# Entry expiration in milliseconds. By default the entries never expire.
spring.cache.redis.time-to-live=1d
# Whether to use the key prefix when writing to Redis.
spring.cache.redis.use-key-prefix=true
第三步:
編寫RedisConfig檔案:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
import java.time.Duration;
//@EnableCaching 【重點】不能加,不然@Cacheable無效
@Configuration
//載入該字首的配置資訊,提供set方法即可自動注入
@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisConfig extends CachingConfigurerSupport{
private static final Logger log = LoggerFactory.getLogger(RedisConfig.class);
private Duration timeToLive = Duration.ZERO;
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}
/**
* 自定義生成redis-key
*
* @return
*/
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName()).append(".");
sb.append(method.getName()).append(".");
for (Object obj : objects) {
sb.append(obj.toString());
}
log.info("------> 自定義生成redis-key完成,keyGenerator=" + sb.toString());
return sb.toString();
}
};
}
/* @Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
//【重點】【new RedisCacheManager()在 springboot2.x 裡無效】
RedisCacheManager manager = new RedisCacheManager(redisTemplate);
manager.setUsePrefix(true);
RedisCachePrefix cachePrefix = new RedisPrefix("prefix");
manager.setCachePrefix(cachePrefix);
// 整體快取過期時間
manager.setDefaultExpiration(3600L);
// 設定快取過期時間。key和快取過期時間,單位秒
Map<String, Long> expiresMap = new HashMap<>();
expiresMap.put("user", 1000L);
manager.setExpires(expiresMap);
return manager;
}*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(this.timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
.disableCachingNullValues();
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
log.info("------> 自定義RedisCacheManager載入完成");
return redisCacheManager;
}
@Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
log.info("------> 自定義RedisTemplate載入完成");
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
springboot2.X RedisCacheManager已經沒有了單引數的構造方法 ,參考文件
第四步:
註解需要快取的地方
@Service("UserService")
//對應ehcache.xml檔案裡面的<cache name="user">
@CacheConfig(cacheNames = "user")
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
//無論怎樣,都將方法的返回結果放到快取當中。
//ehcache3 會自動處理要快取的key-value裡面的key
@CachePut
public void save(User user) {
System.out.println("新增功能,更新快取,直接寫庫, id=" + user);
userRepository.save(user);
}
@Override
@CachePut
public void update(User user) {
System.out.println("更新功能,更新快取,直接寫庫, id=" + user);
userRepository.save(user);
}
@Override
//將一條或者多條資料從快取中刪除。
@CacheEvict
public void remove(Long userId) throws Exception {
System.out.println("刪除功能,刪除快取,直接寫庫, id=" + userId);
userRepository.deleteById(userId);
}
@Override
@CacheEvict
public void batchRemove(Long[] userIds) throws Exception {
for (Long userId : userIds) {
userRepository.deleteById(userId);
}
}
@Override
public List<User> getUserList() {
return userRepository.findAll();
}
@Override
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
//在方法執行前Spring先是否有快取資料,如果有直接返回。如果沒有資料,呼叫方法並將方法返回值存放在快取當中。
@Cacheable
public User findById(long id) {
//可以在這裡打一個斷點,發現第二次查詢不會被斷點卡到為配置成功
System.out.println("查詢功能,快取找不到,直接讀庫, id=" + id);
return userRepository.findById(id);
}
}
第五步:
配置啟動檔案:
@SpringBootApplication
@EnableCaching//開啟快取
@PropertySource("application.properties")
public class XXXApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(XXXApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(XXXApplication.class, args);
}
}
可能出現的問題:
1、 型別轉換出現問題:看看是不是工程裡面加入了熱部署,註釋掉熱部署即可
java.lang.ClassCastException:
com.XXX.system.UserDO cannot be cast to com.XXX.system.UserDO
2、這裡涉及redis的常見操作:
檢視key的型別
type key
檢視所有key
keys *
刪除一個key
DEL key
清空一個DB
FLUSHDB
清空所有
FLUSHALL
切換到其他DB
select 1
可能的問題:
WRONGTYPE Operation against a key holding the wrong kind of value
原因是:型別不一致,使用redis命令不當造成的。
例如:
127.0.0.1:6379> keys *
1) "user~keys"
2) "user_1"
127.0.0.1:6379> get user~keys
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type user~keys
zset
127.0.0.1:6379> zrange user~keys 0 10
1) "user_1"
參考文件: