缓存存在的意义?
(1)减轻频繁读取数据库的压力
(2)一些临时性的数据,比如5min就会失效的短信验证码,就不必要放在数据库中,完全可以放在缓存中去实现
Spring Boot的缓存
Spring从3.1开始定义Cache和CacheManager接口统一不同的缓存技术,并支持JCache(JSR-
107)注解来简化我们的开发
缓存的一些基础注解概念如图:
Cache,CacheManager,@Cacheable,@CacheEvict,@CachePut,@EnableCachsing,如下图:
比如@Cacheable这个注解,声明在函数的前面,则该函数的查询条件之前执行过的话,就会从缓存中取数据了。
@CacheEvict用来清除缓存,这个可以声明在delete(),update()方法的前面
@CachePut:用于更新缓存,该注解既保证方法被调用,又保证数据能放入缓存
使用MyBatis注解版连接操作MySQL数据库
第一步:新加一个包含cache、Web、MySQL启动器的项目
第二步:配置项目属性 数据源,数据库名后面的参数是为了避免出现连接跑SQL乱码报错:
#set mysql connetion setting
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_cache_demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
# not necessary
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 开启驼峰命名规匹配规则
mybatis.configuration.map-underscore-to-camel-case=true
# 指定某个包下类的log级别
logging.level.com.stephanie.cache.mapper=debug
第三步:自定扫描mapper的包路径,注意注解@MapperScan:
package com.stephanie.cache;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 搭建缓存开发demo的基本环境
* 步骤:
* 1.创建数据库,测试表department,employee,我本地数据库名spring_boot_cache_demo
* 2.创建Java Bean 实体对象
* 3.整合MyBatis操作数据库
* (1)配置数据源信息
* (2)使用注解版的MyBatis
* (a)@MapperScan指定MyBatis需要扫描的所有mapper包指定包路径,以及每个被扫描的类都需要加注解@Mapper
*/
@MapperScan("com.stephanie.cache.mapper")
@SpringBootApplication
public class SpringBootDemoforCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoforCacheApplication.class, args);
}
}
第四步:写Mapper接口类,注意注解@Mapper
package com.stephanie.cache.mapper;
import com.stephanie.cache.bean.Employee;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
@Mapper
public interface EmployeeMapper {
@Select("SELECT * FROM employee WHERE id = #{id}")
public Employee getEmpById(Integer id);
@Update("update employee set lastName=#{lastName}," +
"email=#{email}, gender=#{gender} WHERE id = #{id}")
public void updateEmp(Integer id);
@Delete("delete FROM employee WHERE id = #{id}")
public void deleteEmpById(Integer id);
@Insert("insert into employee(lastName,email,gender,dId) " +
" values(#{lastName}, #{email}, #{gender}, #{dId})")
public void insertEmp(Integer id);
}
第五步,编写Service层,注意@Service注解:
package com.stephanie.cache.service;
import com.stephanie.cache.bean.Employee;
import com.stephanie.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import javax.xml.ws.ServiceMode;
@Service
public class EmployeeService {
//首次添加@Autowired这个注解可能会报错,需要修改一下报错级别
@Autowired
EmployeeMapper employeeMapper;
//
public String employee()
{
Employee employee = employeeMapper.getEmpById(1);
System.out.println("start emp="+employee.getStr()+"@");
return employee.getStr();
}
}
第六步:编写一个测试类用来测试查询SQL:
package com.stephanie.cache.controller;
import com.stephanie.cache.bean.Employee;
import com.stephanie.cache.mapper.EmployeeMapper;
import com.stephanie.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@ResponseBody
@RequestMapping("/employee")
public String employee()
{
return employeeService.employee();
}
}
运行效果,如下图:
OK!在基础的环境准备就绪之后,开始简单的缓存学习!
缓存使用步骤:
第一步:在启动类中指定开启缓存:
/**
* 第二部分:学习使用缓存
* 1.开启基于注解的缓存
* 2.标注缓存注解即可
*/
//开启基于注解的缓存
@EnableCaching
@MapperScan("com.stephanie.cache.mapper")
@SpringBootApplication
public class SpringBootDemoforCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoforCacheApplication.class, args);
}
}
第二步:在Service业务层实现缓存的具体方法,即使用该方法查询数据的时候,有机会从缓存中取数(当多次查询相同id的员工数据时,且走缓存的时候,不会有任何log显示。即:只有第一次查询或缓存数据为空的时候 才会有请求log和mybatis查询log。)
/**
* service层测试缓存的接口
* 缓存的几个属性:
* (1)cacheNames/value: 指定缓存的名字,区分是哪个类型的缓存数据
* (2)key:缓存数据使用的key,可以用它来指定,默认是会使用方法参数的值,每个缓存组件有自己唯一的名字.
* 以下key="#id"表示参数id的值,#a0, #a1..可以指定其他字段作为缓存的主键!
* (3)keyGenerator:key的生成器,可以指定自己key的生成器
* key或keyGenerator 二选一使用
* (4)cacheManager:指定缓存管理器,或者cacheResolver指定获取解析器
* (5)condition:指定缓存在某个条件下才会生效 工作
* (6)unless:否定缓存,即:如果满足某个条件,就不缓存数据了,具体对于条件的编写,还可以取查询结果用来写条件
* @param id
* @return
*/
@Cacheable(cacheNames = "employee", key="#id",condition="#id>0")
public Employee getEmpfromCache(Integer id)
{
System.out.println("查询id="+id+"的员工信息:");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
此外,关于key的具体补充写法说明:
Cacheable注解的参数 key的值可以指定的匹配项 关键字,默认普通用法是key="#id"最普通的使用本表的id去识别数据,其他指定key方式如下图:
缓存的工作原理:(看这一段的时候我走神了。。)
(1)缓存的自动配置类:CacheAutoConfiguration
(2)缓存的配置类
(3)那个配置类生效:SImpleCacheConfiguration
(4)给容器中注册了一个CacheManager、ConcurrentMapCacheManager
(5)可以获取和创建ConcurrentMapCache类型的缓存组件,作用是将数据
@Cacheable注解的运行流程:
(1)查询缓存组件Cache,按照cacheNames指定的名字获取。第一次获取缓存如果没有缓存组件就创建一个Cache组件;
(2)去Cache中查找缓存,使用一个key,默认key是方法的参数;
(3)查不到缓存就调用目标查询方法
(4)将目标方法返回的结果,放进缓存中