缓存

搭建基本环境

  1. 导入数据文件。创建department和employee表

  2. 创建JavaBean封装数据

  3. 整合Mybatis操作数据库

    1. 配置数据源信息
    2. 使用注解版的Mybatis
      1. @MapperScan指定需要扫描的mapper接口所在的包
  4. 开启缓存

    1. 开启基于注解的缓存
    2. 标注缓存注解即可
    @SpringBootApplication
    @MapperScan("com.whut.mapper")
    @EnableCaching
    public class SpringBootCacheApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(SpringBootCacheApplication.class, args);
       }
    
    }
    

一级缓存

将方法的运行结果进行缓存,以后要相同的数据,直接从缓存中获取,不用调用方法

  1. CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字

  2. 几个属性

    1. cacheNames/value:指定缓存组件的名字

    2. key:缓存数据使用的key,可以用它来指定。默认是使用方法参数的值 1-方法的返回值

      1. 编写SpEL:#id,参数id的值 #a0 #p0 #root.args[0]
        在这里插入图片描述
      @Cacheable(cacheNames = {"emp"},key = "#root.methodName+'['+#id+']'")
      public Employee getEmpById(Integer id){
         Employee empById = employeeMapper.getEmpById(id);
         System.out.println("查询"+id+"号员工");
         return empById;
      }
      
    3. keyGenerator:key的生成器。可以自己指定key的生成器的组件id。(key/keyGenerator二选一)

    @Configuration
    public class MyCacheConfig {
       @Bean("myKeyGenerator")
       public KeyGenerator keyGenerator(){
    
          return new KeyGenerator() {
                @Override
                public Object generate(Object o, Method method, Object... objects) {
                   return  method.getName()+"【"+ Arrays.asList(objects).toString()+"】";
                }
          };
       }
    }
    
    @Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator")
     public Employee getEmpById(Integer id){
         Employee empById = employeeMapper.getEmpById(id);
         System.out.println("查询"+id+"号员工");
         return empById;
     }
    
    1. cacheManager:指定缓存管理器,或者cacheResolver指定获取解析器
    2. condition:指定符合条件的情况下才缓存
      condition = “#a0>1”:第一个参数的值大于1的时候才进行缓存
    3. unless:否定缓存。当unless指定的条件为true。方法的返回值就不会被缓存,可以获取到结果进行判断
      1. unless = “#result == null”
    4. sync:是否使用异步模式,异步模式下unless不支持
    @Cacheable(cacheNames = {"emp"})
     public Employee getEmpById(Integer id){
         Employee empById = employeeMapper.getEmpById(id);
         System.out.println("查询"+id+"号员工");
         return empById;
     }
    
  3. 原理

    1. 自动配置类:CacheAutoConfiguration
    2. 缓存的配置类
      1. 0 = “org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration”
      2. 1 = “org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration”
      3. 2 = “org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration”
      4. 3 = “org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration”
      5. 4 = “org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration”
      6. 5 = “org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration”
      7. 6 = “org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration”
      8. 7 = “org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration”
      9. 8 = “org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration”
      10. 9 = “org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration”
    3. 默认生效的配置类
      1. SimpleCacheConfiguration
    4. 给容器中注册了一个CacheManager:ConcurrentMapCacheManager
    5. 可以获取和创建ConcurrentMapCache类型的缓存组件,它的作用将数据保存在ConcurrentMap中
  4. 运行流程

    1. 方法运行之前,先去查询cache(缓存组件),按照cacheName指定的名字获取;CacheManager先获取相应的缓存,第一次获取缓存如果没有cache组件会自动创建
    public Cache getCache(String name) {
       Cache cache = (Cache)this.cacheMap.get(name);
       if (cache == null && this.dynamic) {
          synchronized(this.cacheMap) {
                cache = (Cache)this.cacheMap.get(name);
                if (cache == null) {
                   cache = this.createConcurrentMapCache(name);
                   this.cacheMap.put(name, cache);
                }
          }
       }
       return cache;
    }
    
    1. 去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;key是按照某种策略生成的,默认是使用keyGenerator生成的,默认使用的是SimpleKeyGenerator生成key
    public static Object generateKey(Object... params) {
       if (params.length == 0) {
          //如果没有参数,就new SimpleKey()
          return SimpleKey.EMPTY;
       } else {
          if (params.length == 1) {  //如果只有一个参数key=参数值
                Object param = params[0];
                if (param != null && !param.getClass().isArray()) {
                   return param;
                }
          }
          //如果有多个参数,返回new SimpleKey(params);
          return new SimpleKey(params);
       }
    }
    
    1. 没有查到缓存就调用目标方法
    2. 将目标方法返回的结果,放到缓存中
  5. @CachePut即调用方法,又更新缓存数据。修改了数据库的数据,同时更新缓存

    1. 先调用目标方法
    2. 将目标方法的结果缓存起来
    @CachePut(value = "emp",key = "#employee.id")
     public Employee updateEmployee(Employee employee){
         employeeMapper.updateEmployee(employee);
         return employee;
     }
    
    1. 查询缓存和更新缓存的key需要一致
  6. @CacheEvict:缓存清除

    1. 可删除指定key
    2. allEntries = true:指定清除这个缓存中的所有数据
    3. beforeInvocation = false:缓存的清除是否在方法之前执行,默认缓存清除操作在方法之后执行,如果出现异常,缓存就不会执行
  7. @Caching,定义复杂的缓存规则

  8. @CacheConfig,可以抽取缓存的公共配置

整合redis来进行缓存

  1. 安装redis:使用docker
  2. 导入redis-starter
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. Redis常见的五大数据类型
    1. String[字符串]:stringRedisTemplate.opsForValue()
    2. List[列表]:stringRedisTemplate.opsForList()
    3. Set[集合]:stringRedisTemplate.opsForSet()
    4. Hash[散列]:stringRedisTemplate.opsForHash()
    5. ZSet[有序集合]:stringRedisTemplate.opsForZSet()
  2. 配置redis
spring.redis.host=10.138.227.212
  1. 保存对象,需实现序列化接口
@Autowired
RedisTemplate redisTemplate;

@Test
public void testRedis(){
   Employee empById = employeeMapper.getEmpById(1);
   //默认如果保存对象,使用jdk序列化机制,序列化后的数据保存到redis中
   redisTemplate.opsForValue().set("emp-01",empById);
}

在这里插入图片描述

  1. 自定义序列化规则
@Configuration
public class MyRedisConfig {
    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Employee> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Employee.class);
        template.setDefaultSerializer(jsonRedisSerializer);
        return template;
    }
}

测试

@Autowired
RedisTemplate<Object,Employee> empRedisTemplate;

@Test
public void testRedis(){
   Employee empById = employeeMapper.getEmpById(1);
   empRedisTemplate.opsForValue().set("emp-01",empById);
   //1. 将数据以json的方式保存
   //(1)自己将对象转为json
   //(2)redisTemplate默认的序列化规则
}

在这里插入图片描述

  1. 缓存测试

    1. 原理:CacheManager==Cache 缓存组件来实际给缓存中存取数据

      1. 引入redis的starter,容器中保存的是 RedisCacheManager
      2. RedisCacheManager帮我们创建RedisCache来作为缓存组件,通过redis来缓存数据的
      3. 默认保存数据K-v都是Object:利用序列化保存,如何保存为json
        1. 引入redis的starter,CacheManager变为 RedisCacheManager
        2. 默认创建的RedisCacheManager操作redis的时候使用的是RedisTemplate<Object, Object>
        3. RedisTemplate<Object, Object>默认使用的是jdk序列化机制
      4. 自定义CacheManager
      @Bean
      public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
         RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Employee.class)));
         return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
      }
      
      • 注:缓存失败,出现500错误,清除之前的jdk序列化缓存即可
        在这里插入图片描述
    2. 反序列化问题

      1. 缓存的数据能存入redis,第二次从缓存中查询就不能反序列化回来。
      2. 解决:使用多个cacheManager
       @Bean
      public RedisCacheManager employeeCacheManager(RedisConnectionFactory factory) {
         RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Employee.class)));
         return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
      }
      @Bean
      public RedisCacheManager departmentCacheManager(RedisConnectionFactory factory) {
         RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Department.class)));
         return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
      }
      
      @CacheConfig(cacheManager = "departmentCacheManager")
      @Service
      public class DepartmentService {
       
      }
      
      @CacheConfig(cacheManager = "employeeCacheManager")
      @Service
      public class EmployeeService {
      
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一大岐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值