SpringBoot学习之Spring缓存抽象(1)

7 篇文章 0 订阅
3 篇文章 0 订阅
1.简单的整合项目

SpringBoot+Spring web+mybatis+cache+mysql
在这里插入图片描述

1)application.properties
#连接Mysql
spring.datasource.url=jdbc:mysql://localhost:3306/s003?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#开启驼峰命名法
mybatis.configuration.map-underscore-to-camel-case=true
2)bean实例

Employee :

package cronos.study_g01_cacher.bean;

/**
 * @ClassName: Employee
 * @Author: cronos
 * @Version: 1.0
 **/
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;
    private Integer dId;

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", dId=" + dId +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Integer getdId() {
        return dId;
    }

    public void setdId(Integer dId) {
        this.dId = dId;
    }
}

3)创建mapper接口映射数据库
package cronos.study_g01_cacher.mapper;

import cronos.study_g01_cacher.bean.Employee;
import org.apache.ibatis.annotations.*;

/**
 * @ClassName: EmployeeMapper
 * @Author: cronos
 * @Version: 1.0
 **/
@Mapper
public interface EmployeeMapper {
    //根据员工id查询员工信息
    @Select("select * from employee where id = #{id}")
    public Employee getEmpById(Integer id);
    //更新员工信息
    @Update("update employee set lastName = #{lastName},email = #{email},gender = #{gender},d_id = #{dId} where id = #{id}")
    public void updateEmp(Employee employee);
    //删除员工信息
    @Delete("delete from employee where id = #{id}")
    public void delEmp(Integer id);
    //添加员工信息
    @Insert("insert into employee(lastName,email,gender,d_id) values (#{lastName},#{email},#{gender},#{dId})")
    public void addEmp(Employee employee);
    //根据员工名查询员工信息
    @Select("select * from employee where lastName = #{lastName}")
    public Employee getEmpByLastName(String lastName);
}

4)主程序配置
package cronos.study_g01_cacher;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
 * @MapperScan指定需要扫描的mapper接口所在包
 * @EnableCaching开启基于注解缓存
 */
@MapperScan("cronos.study_g01_cacher.mapper")
@EnableCaching
@SpringBootApplication
public class StudyG01CacherApplication {
    public static void main(String[] args) {
        SpringApplication.run(StudyG01CacherApplication.class, args);
    }
}

5)创建service 实现mapper方法
package cronos.study_g01_cacher.service;

import cronos.study_g01_cacher.bean.Employee;
import cronos.study_g01_cacher.mapper.EmployeeMapper;
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.cache.annotation.Caching;
import org.springframework.stereotype.Service;

/**
 * @ClassName: EmployeeService
 * @Author: cronos
 * @Version: 1.0
 **/
@Service
public class EmployeeService {
    @Autowired
    EmployeeMapper employeeMapper;

    @Cacheable(cacheNames = {"emp"})
    public Employee getEmpById(Integer id){
        System.out.println("查询"+id+"员工信息");
        Employee employee = employeeMapper.getEmpById(id);
        return employee;
    }
    
    @CachePut(value = "emp",key = "#result.id")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }
    
    @CacheEvict(value = "emp",key = "#id")
    public void delEmp(Integer id){
        //偷懒:输出即代表删除一条数据
        System.out.println("delEmp"+id+"员工");
    }

    /**
     * 指定多个缓存规则
     */
    @Caching(
            cacheable = {
                    @Cacheable(value = "emp",key="#lastName")
            },
            put = {
                    @CachePut(value = "emp",key = "#result.id"),
                    @CachePut(value = "emp",key = "#result.gender")
                    //@CachePut的存在会让如下方法函数一定执行的
            }
    )
    public Employee getEmpByLastName(String lastName){
        System.out.println("getEmpByLastName");
        return employeeMapper.getEmpByLastName(lastName);
    }
}

6)controller测试
package cronos.study_g01_cacher.controller;

import cronos.study_g01_cacher.bean.Employee;
import cronos.study_g01_cacher.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName: EmployeeController
 * @Author: cronos
 * @Version: 1.0
 **/
@RestController
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;

    //根据id查询
    @GetMapping("/emp/{id}")
    public Employee getEmpById(@PathVariable("id") Integer id){
        Employee employee = employeeService.getEmpById(id);
        return employee;
    }
    //更新员工
    @GetMapping("emp")
    public Employee updateEmp(Employee employee){
        employeeService.updateEmp(employee);
        return employee;
    }
    //删除
    @GetMapping("/delEmp")
    public String delEmp(Integer id){
        employeeService.delEmp(id);
        return "del success";
    }
    //根据名字查询
    @GetMapping("/emp/lastName/{lastName}")
    public Employee getEmpByLastName(@PathVariable("lastName") String lastName){
        return employeeService.getEmpByLastName(lastName);
    }
}

7)目录结构

在这里插入图片描述

2.缓存原理
     * CacheAutoConfiguration
     * 缓存原理:
     * 1.自动配置类CacheAutoConfiguration
     *      导入缓存组件@Import({ CacheConfigurationImportSelector.class})
     * 2.[在application.properties中添加debug=true:可查看缓存配置生效情况]
     * 		 查看缓存配置生效情况可知晓在自动配置导入组件时,只有SimpleCacheConfiguration生效:
     *          SimpleCacheConfiguration给容器注册了一个CacheManager:实际是ConcurrentMapCacheManager
     *              CacheManager可以获取和创建ConcurrentMapCache,作用是可将数据保存在ConcurrentMap中
     *
     * 运行流程:
     * 1.方法运行之前,先查Cache(缓存组件),按照cacheName的指定名字获取;
     * (CacheManager先获取相应的缓存),首次获取缓存如果没有cache组件会自己创建
     * 2.去Cache中查找缓存的内容,使用一个key,默认为方法参数;
     *      key是按照某种策略生成的,默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key
     *          SimpleKeyGenerator生成key策略:
     *              无参数         key=new SimpleKey();
     *              若有一个参数    key=参数值
     *              若多个参数      key=new SimpleKey(params);
     * 3、没有查到缓存就调用目标方法
     * 4、将目标方法返回的结果,放回缓存中
     *
     * 方法执行之前,@Cacheable先来检查缓存中是否有数据,默认按照参数的值作为key去查询缓存:
     *      若有,则取出map的值。
     *      若没有,则运行方法,存入缓存.
     *
     * 核心:
     * 1.使用CacheManager:ConcurrentMapCacheManager按照名字得到Cache:ConcurrentMapCache
     * 2.key使用keyGenerator生成,默认使用SimpleKeyGenerator生成
3.各注解的使用
-1- @Cacheable
     * 将方法的运行结果进行缓存,以后要是再有相同的数据,直接从缓存中获取,不用调用方法
     * CacheManager中管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每个缓存组件都有自己的唯一名字
     * @Cacheable:
     *      属性:
     *      1.CacheName/value:指定存储缓存组件的名字
     *      2.key:缓存数据使用的key,可以使用它来指定。默认是使用方法参数的值,1-方法的返回值
     *          编写Spel表达式:#id 参数id的值, #a0/#p0 #root.args[0]
     *      3.keyGenerator:key的生成器,可以自己指定key的生成器的组件id
     *      【key/keyGendertor二选一】
     *
     *      4.cacheManager指定Cache管理器,或者cacheReslover指定获取解析器
     *      5.condition:指定符合条件的情况下,才缓存;
     *          eg:condition = "#id>0"
     *      6.unless(除非):否定缓存,unless指定的条件为true,方法的返回值就不会被缓存,可以获取到结果进行判断
     *          eg:unless = "#result==null"
     *      7.sync:是否使用异步模式,unless不支持
	//Service 
    @Cacheable(cacheNames = {"emp"})
    public Employee getEmpById(Integer id){
        System.out.println("查询"+id+"员工信息");
        Employee employee = employeeMapper.getEmpById(id);
        return employee;
    }
    
	//Controller
	//根据id查询
    @GetMapping("/emp/{id}")
    public Employee getEmpById(@PathVariable("id") Integer id){
        Employee employee = employeeService.getEmpById(id);
        return employee;
    }
-1-1-CacheName/value:
	 * CacheName/value:将方法返回结果放置哪个缓存中 为数组方式
//将方法返回结果放置在名叫emp的数组中
@Cacheable(cacheNames = {"emp"} )

访问http://localhost:8080/emp/1
—| 首次访问控制台输出查询语句
在这里插入图片描述
—| 再次访问控制台不再输出查询语句,直接从缓存中取
两次访问内容:
在这里插入图片描述

-1-2-key:

1自定义key:

     * key:默认是使用方法参数的值,还可以自定义key
     *          自定义key :    key="#root.methodName+'['+#id+']'" ===>getEmpById[id]作为key
    @Cacheable(cacheNames = {"emp"},key="#root.methodName+'['+#id+']'")

访问http://localhost:8080/emp/1
调试可以发现key为自定义格式
在这里插入图片描述
2默认key:
默认key为使用方法参数的值

    @Cacheable(cacheNames = {"emp"},key="#id")

访问http://localhost:8080/emp/1
调试可以发现key为方法参数值
在这里插入图片描述

-1-3-keyGenerator:
     * keyGenerator:自定义生成器 在config包下创建自定义配置类
     *          eg:keyGenerator="myKeyGenerator"
     *          @Cacheable(cacheNames = {"emp"},keyGenerator="myKeyGenerator")
//自定义生成器 在config包下创建自定义配置类
package cronos.study_g01_cacher.config;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @ClassName: MyCacheConfig
 * @Author: cronos
 * @Version: 1.0
 **/
@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")

访问http://localhost:8080/emp/1
调试可以发现key为自定义生成器格式
在这里插入图片描述

-1-4-condition:
     * condition:
     *          eg:condition = "#a0>1" 表示第一个参数大于1才进行缓存
     *          @Cacheable(cacheNames = {"emp"},condition = "#a0>1")
-1-5-unless:
     * unless:
     *          eg:unless = "#a0==2" 表示第一个参数值为2 结果不缓存
     *          @Cacheable(cacheNames = {"emp"},unless = "#a0==2")
-1-6-sync:
     * sync:异步模式 开启后 unless不能使用
-2-@CachePut
	 * @CachePut: 既调用方法 又更新缓存数据 同步更新缓存
     * 运行步骤:
     *  先运行方法,再将目标结果缓存起来
	//Service
    @CachePut(value = "emp",key = "#result.id")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

	//Controller
    //更新员工
    @GetMapping("emp")
    public Employee updateEmp(Employee employee){
        employeeService.updateEmp(employee);
        return employee;
    }
     *  测试步骤
     * 1、查询2号员工---->查询结果会放到缓存中 再次查询直接调用缓存
     * 2、更新2号员工
     * 3、查询2号员工---->还是原来的数据
     *      原因:
     *      查询和更新的key不同:
     *          查询2号员工:key:2 value:张三
     *          更新2号员工:返回值放入缓存 key:传入employee对象 value:返回employee对象
     *      解决:
     *          key="#employee.id" 使用传入参数员工id 和查询key统一
     *          key="#result.id"
     * 			@CachePut(value = "emp",key = "#result.id")
     *          注:@Cacheable中的key不能使用"#result.id",因为Cacheable是先缓存再运行
     *      效果:
     *      第一次查询:查询mysql
     *      第二次更新:更新mysql
     *      第三次查询:调用内存
-3-@CacheEvict
     * @CacheEvict: 缓存清除
     * allEntries = true,清除所有缓存数据 默认false
     * beforeInvocation=true 缓存清除是否在方法前执行
     *                       true 若程序异常 依旧清除缓存
     *                       默认false 若程序异常 则不会清除缓存
	//Service
    @CacheEvict(value = "emp",key = "#id")
    public void delEmp(Integer id){
        //偷懒:输出即代表删除一条数据
        System.out.println("delEmp"+id+"员工");
    }

	//Controller
	    //删除
    @GetMapping("/delEmp")
    public String delEmp(Integer id){
        employeeService.delEmp(id);
        return "del success";
    }

删除的同时也将缓存删除
1.若allEntries = true则删除所有缓存
2.若beforeInvocation=true则删除缓存在方法执行前=>失败也执行,beforeInvocation=flase删除缓存在方法执行后=>失败不执行

-4-@Caching

组合
Cacheable
CachePut
CacheEvict

	//Service
	//指定多个缓存规则
    @Caching(
            cacheable = {
                    @Cacheable(value = "emp",key="#lastName")
            },
            put = {
                    @CachePut(value = "emp",key = "#result.id"),
                    @CachePut(value = "emp",key = "#result.gender")
                    //@CachePut的存在会让如下方法函数一定执行的
            }
    )
    public Employee getEmpByLastName(String lastName){
        System.out.println("getEmpByLastName");
        return employeeMapper.getEmpByLastName(lastName);
    }
    
	//Controller
    //根据名字查询
    @GetMapping("/emp/lastName/{lastName}")
    public Employee getEmpByLastName(@PathVariable("lastName") String lastName){
        return employeeService.getEmpByLastName(lastName);
    }

访问http://localhost:8080/emp/lastName/张三执行一次语句查询 并放入缓存中;
访问http://localhost:8080/emp/2查询id=2的员工信息时 直接从缓存中取id=2的员工信息(即张三)
注:
再次访问http://localhost:8080/emp/lastName/依旧会执行一次语句查询 因为**@CachePut**的存在会让方法函数一定执行

补充:

CacheConfig抽取缓存的公共配置
Service:

@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {}

Service中注解的value=emp就可省略

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值