springBoot整合redis(做緩存)

springBoot整合Redis


1,配置Redis配置類

package org.redislearn.configuration;

import java.lang.reflect.Method;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

@EnableCaching
@Configuration
public class RedisConfiguration extends CachingConfigurerSupport{
    /*
     * key的生成策略(根據目標對象,本例是service實現類,以及其中的方法,參數拼接成一個key)
     * 能夠根據業務需求更改策略
     */
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            /*
             * 參數1:要操做的目標對象
             * 參數2:要操做的方法
             * 參數3:執行方法時的參數
             */
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sBuilder = new StringBuilder();
                sBuilder.append(target.getClass().getName());
                sBuilder.append(".");
                sBuilder.append(method.getName());    //生成key
                for(Object object:params){
                    sBuilder.append(".");
                    sBuilder.append(object.toString());
                }
                return sBuilder.toString();
            }
        };
    }
    /**
     * redisTemplate相關配置  RedisTemplate操做Redis
     * @param factory
     * @return
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template  = new RedisTemplate<>();
        //設置工廠
        template.setConnectionFactory(factory);
        //使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值
//(默認使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer<>(Object.class);
        //建立對象映射
        ObjectMapper mapper = new ObjectMapper();
        //指定要序列化的域,field,get和set,以及修飾符範圍,ANY是都有包括private和public
        mapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
        //指定序列化輸入的類型,類必須是非final修飾的,final修飾的類,好比String,Integer
//等會拋出異常
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //
        jackson2JsonRedisSerializer.setObjectMapper(mapper);
        //字符串序列化器
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key採用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也採用String的方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value採用jackson的方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value也採用Jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

2,配置application.yml配置文件

server:
  port: 8080
mybatis:
  config-location: classpath:mybatis-config.xml
  type-aliases-package: org.xxxx.entity
  mapper-locations:
    - classpath:mapper/*.xml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
    username: root
    password: 123456
  redis:
    host: 127.0.0.1
    port: 6379
    jedis:
      pool:
        max-active: 10
        max-idle: 5
        min-idle: 2
        max-wait: -1

3,操做Redis數據庫

  • RedisTemplate<String,Object> :相似於Mybatis的SqlSession,用來操做數據庫的對象
//在須要使用的地方利用spring注入RedisTemplate對象
@Autowired
private RedisTemplate<String,Object> redisTemplate;

4,使用Redis做緩存

  • 新建一個實體類

ps:存入redis的實體類數據須要實現序列化接口,否則會報org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; 至於原理,能夠看看我看過的這篇文章瞭解一下
Redis爲何須要序列化https://www.zhihu.com/question/277363840/answer/392945240java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {//redis need to serialize
    private int pid;
    private String pname;
    private double price;
    private int store;
    private List<String> images;
    private String detail;
}
  • (默認已經寫好了mapper,service等接口和相關配置)
  • 在service實現類中的方法加註解
    • @Cacheable("xxx") //數據以xxx開頭存入緩存
    • @CacheEvict(value = {"xxx","xxx"},allEntries = true) //清除緩存中的xxx開頭的數據
    @Service
    public class ProductServiceImpl implements ProductService {
    
        @Autowired
        private ProductMapper productMapper;
    
        @Cacheable("findAll")
        @Override
        public List<Product> all() {
            return productMapper.all();
        }
    
        @Cacheable("findById")
        @Override
        public Product findProductByPid(int pid) {
            return productMapper.findProductByPid(pid);
        }
    
        //把跟這個id有關的數據都要刪除,這裏是"findAll"和"findById"
        @CacheEvict(value = {"findAll","findById"},allEntries = true)
        @Override
        public int deleteProductByPid(int pid) {
            return productMapper.deleteProductByPid(pid);
        }
    }
    • 這裏的清除緩存的數據配置有問題,當執行刪除方法的時候會將以"findAll","findById"開頭的key的其餘數據都清除掉,這樣以後的查詢又是從數據庫查,性能再次降低
      解決方案:
    • 寫一個工具類,生成對應的key,用來精準刪除
    public class RedisUtil {
        public static String generate(String namespace,Object target, String method, Object... params) {
            StringBuilder sBuilder = new StringBuilder();
            sBuilder.append(namespace);
            sBuilder.append("::");  //自動生成的key會有雙冒號
            sBuilder.append(target.getClass().getName());
            sBuilder.append(".");
            sBuilder.append(method);    //生成key
            for(Object object:params){
            sBuilder.append(".");
            sBuilder.append(object.toString());
            }
            return sBuilder.toString();
        }
    }
    • 改進service實現類的方法
    //定義一個須要的命名空間
    private static final String NAMESPACE = "findProductByPid";
    
    //刪除指定的key
    @CacheEvict(value = "findAll",allEntries = true)
    @Override
    public int deleteProductByPid(int pid){
        //前綴 this method 參數
        String key = RedisUtil.generate(NAMESPACE,this,"findProductByPid",pid);
        System.out.println(key);
        //操做刪除指令
        redisTemplate.delete(key);
        return productMapper.deleteProductByPid(pid);
    }
    • thinking :若是想要保留redis中的findAll的查詢,那麼能夠在封裝數據的時候把數據封裝成hash,就不用list,經過對象名 的 filed刪除這個數據,查詢findAll的時候就仍是從redis中來數據。但通常狀況再從數據庫查這一次影響不大,除非,刪查很是頻繁

至此,如有紕漏,望各位不吝賜教mysql

相關文章
相關標籤/搜索