2020.03.17 springboot缓存相关
出自尚硅谷雷峰阳老师讲解的Spring Boot整合篇,地址:
http://www.atguigu.com/download_detail.shtml?v=38
1、JSR107规范
CachingProvider【管理CacheManager】、CacheManager【管理Cache】、Cache【一个cache仅被一个CacheManager管理】、Entry【一个个缓存的内容】、Expiry【有效期】
2、Spring的缓存抽象
注意两点:
①确定方法需要被缓存以及他们的缓存策略
②从缓存中读取之前缓存存储的数据
缓存注解及作用:
@EnableCaching:开启基于注解的缓存
@Cacheable:主要针对方法配置,能够根据方法的请求参数对其结果进行缓存【主要用于查询】
@CachePut:保证方法被调用,又希望结果被缓存。【主要用于修改】
@CacheEvict:清空缓存【主要用于删除】
SPEL表达式:
名字
|
位置
|
描述
|
示例
|
methodName
|
root object | 当前被调用的方法名 | #root.methodName |
result
|
evaluation context
|
方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache put’的表达式 ’cache evict’的表达式beforeInvocation=false)
|
#result
|
argument name
|
evaluation context
|
方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引;
|
#iban 、 #a0 、 #p0 |
3、缓存使用
pom文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.study.springboot</groupId> <artifactId>cache-study</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cache-study</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- 缓存相关 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 数据库相关 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <!-- mybatis和springboot整合 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <!-- 数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.16</version> </dependency> <!-- 日志 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
主启动类:
package com.study.springboot.cache; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching public class CacheStudyApplication { public static void main(String[] args) { SpringApplication.run(CacheStudyApplication.class, args); } }
yml文件:
server:
port: 8001
tomcat:
uri-encoding: UTF-8
spring:
application:
name: springboot-cache
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot-cache?useUnicode=true&characterEncoding-utf-8&useSSL=false&serverTimezone=UTC #需要加时区
username: root
password: 自己的密码
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
http:
encoding:
charset: utf-8
force: true
enabled: true
redis:
host: 192.168.213.128#自己的redis主机
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities
configuration:
map-underscore-to-camel-case: true
controller:
package com.study.springboot.cache.controller; import com.study.springboot.cache.entity.Employee; import com.study.springboot.cache.service.EmployeeService; import lombok.extern.slf4j.Slf4j; 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; @RestController @Slf4j public class EmpController { @Autowired private EmployeeService employeeService; @GetMapping("/emp/{id}") public Employee getEmpById(@PathVariable("id") Integer id){ return employeeService.queryById(id); } }
------------------------------------------------------------------------------
package com.study.springboot.cache.controller;
import com.study.springboot.cache.entity.Department;
import com.study.springboot.cache.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
@GetMapping("/dept/{id}")
public Department getDeptById(@PathVariable("id") Integer id){
return departmentService.getDeptById(id);
}
}
service
package com.study.springboot.cache.service; import com.study.springboot.cache.entity.Employee; import com.study.springboot.cache.mapper.EmployeeMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.stereotype.Service; @Service @Slf4j public class EmployeeService { @Autowired private EmployeeMapper employeeMapper; @Caching( cacheable = @Cacheable(value="emp",cacheManager = "employmentRedisCacheManager") ) public Employee queryById(Integer id){ log.info("查询id为:"+id+"数据"); return employeeMapper.selectEmp(id); } }
------------------------------------------------------------------------------
package com.study.springboot.cache.mapper;
import com.study.springboot.cache.entity.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface DepartmentMapper {
public Department selectDeptById(@Param("id") Integer id);
}
mapper:
package com.study.springboot.cache.mapper; import com.study.springboot.cache.entity.Employee; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @Mapper public interface EmployeeMapper { //根据id查询 public Employee selectEmp(@Param("id") Integer id); //修改 public void updateEmp(Employee employee); }
------------------------------------------------------------------------------
package com.study.springboot.cache.mapper;
import com.study.springboot.cache.entity.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface DepartmentMapper {
public Department selectDeptById(@Param("id") Integer id);
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.study.springboot.cache.mapper.EmployeeMapper"> <!--public Employee selectEmp(@Param("id") Integer id);--> <select id="selectEmp" parameterType="Integer" resultMap="BaseResultMap"> select * from employee where id = #{id} </select> <!-- public void updateEmp(Employee employee); --> <update id="updateEmp" parameterType="com.study.springboot.cache.entity.Employee"> update employee set lastName = #{lastName}, email = #{email}, gender = #{gender}, d_id = #{dId} where id = #{id} </update> <resultMap id="BaseResultMap" type="com.study.springboot.cache.entity.Employee" autoMapping="true"> </resultMap> </mapper>
------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.springboot.cache.mapper.DepartmentMapper">
<!--public Department selectDeptById(@Param("id") Integer id);-->
<select id="selectDeptById" parameterType="Integer" resultMap="BaseResultMap">
select * from department where id = #{id}
</select>
<resultMap id="BaseResultMap" type="com.study.springboot.cache.entity.Department" autoMapping="true">
</resultMap>
</mapper>
只有springboot版本为1.xx时才能自定义,版本为2.xx时没找到对应的构造函数 个人观点【没找到对应的构造函数】
自定义缓存管理器时注意别写get
1 import com.study.springboot.cache.entity.Department; 2 import com.study.springboot.cache.entity.Employee; 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.context.annotation.Primary; 6 import org.springframework.data.redis.cache.RedisCacheManager; 7 import org.springframework.data.redis.connection.RedisConnectionFactory; 8 import org.springframework.data.redis.core.RedisTemplate; 9 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 10 11 import java.net.UnknownHostException; 12 13 14 @Configuration 15 public class MyRedisConfig { 16 17 18 @Bean 19 public RedisTemplate<Object,Employee> getEmployeeRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { 20 RedisTemplate<Object,Employee> template = new RedisTemplate<Object,Employee>(); 21 template.setConnectionFactory(redisConnectionFactory); 22 //自己配置序列化器 23 Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class); 24 template.setDefaultSerializer(serializer); 25 return template; 26 } 27 28 @Bean 29 public RedisTemplate<Object,Department> getDepartmentRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { 30 RedisTemplate<Object,Department> template = new RedisTemplate<Object,Department>(); 31 template.setConnectionFactory(redisConnectionFactory); 32 //自己配置序列化器 33 Jackson2JsonRedisSerializer<Department> serializer = new Jackson2JsonRedisSerializer<Department>(Department.class); 34 template.setDefaultSerializer(serializer); 35 return template; 36 } 37 38 @Bean 39 public RedisCacheManager employmentRedisCacheManager(RedisTemplate<Object,Employee> employeeRedisTemplate){ 40 RedisCacheManager redisCacheManager = new RedisCacheManager(employeeRedisTemplate); 41 redisCacheManager.setUsePrefix(true); 42 43 return redisCacheManager; 44 } 45 46 @Primary 47 @Bean 48 public RedisCacheManager departmentRedisCacheManager(RedisTemplate<Object,Department> departmentRedisTemplate){ 49 RedisCacheManager redisCacheManager = new RedisCacheManager(departmentRedisTemplate); 50 redisCacheManager.setUsePrefix(true); 51 52 return redisCacheManager; 53 } 54 }
测试类:
redis命令网址:http://www.redis.cn/commands.html
package com.study.springboot.cache; import com.study.springboot.cache.entity.Employee; import com.study.springboot.cache.service.EmployeeService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class CacheStudyApplicationTests { @Test public void contextLoads() { } @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private EmployeeService employeeService; @Autowired private RedisTemplate<Object,Employee> employeeRedisTemplate; @Test public void testRedis(){ ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue(); //stringValueOperations.append("msg","hello"); Employee employee = employeeService.queryById(2); //stringValueOperations.append("emp:1",employee.toString()); employeeRedisTemplate.opsForValue().set("emp:1",employee); } }
建表sql(老师提供的课件中有):
/* Navicat MySQL Data Transfer Source Server : 本地 Source Server Version : 50528 Source Host : 127.0.0.1:3306 Source Database : springboot_cache Target Server Type : MYSQL Target Server Version : 50528 File Encoding : 65001 Date: 2018-04-27 14:54:04 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for department -- ---------------------------- DROP TABLE IF EXISTS `department`; CREATE TABLE `department` ( `id` int(11) NOT NULL AUTO_INCREMENT, `departmentName` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for employee -- ---------------------------- DROP TABLE IF EXISTS `employee`; CREATE TABLE `employee` ( `id` int(11) NOT NULL AUTO_INCREMENT, `lastName` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `gender` int(2) DEFAULT NULL, `d_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;