zoukankan      html  css  js  c++  java
  • 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
        password:123456 # 生产环境中需要加上密码,不然数据及其不安全
        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/392945240

    @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中来数据。但一般情况再从数据库查这一次影响不大,除非,删查非常频繁

    至此,若有纰漏,望各位不吝赐教

  • 相关阅读:
    队列的定义与实现(C语言实现)
    在CTime类中重载&lt;&lt;和&gt;&gt;
    华为OJ:统计大写字母个数
    sql server smo
    应用服务器负载平衡集群
    存储过程如何执行的快速
    sql server 分布式事务
    代理服务器
    怎样实现数据库负载均衡集群
    多层插件开发框架
  • 原文地址:https://www.cnblogs.com/0nePlece/p/13228041.html
Copyright © 2011-2022 走看看