使用springboot缓存
1.缓存的作用
1)加速系统的访问,以及提升系统性能;
比如:
如果商品信息存在数据库中;
每次访问都要查询数据库,耗时;并且访问量大时,系统压力大;
可以将这类热点数据存入缓存;
当访问数据时,先从缓存中找,如果缓存中没有,就从数据库找到并放入缓存方便下次查找;
使用缓存可以提升性能,因为应用程序和缓存的交互相对较快;
2)保存临时数据
比如:
手机验证之类的临时信息,通常需要在几分钟之内删除;
如果存入数据库,不划算,影响系统性能;
可以将这类数据存入缓存;
2.JSR-107
缓存的使用非常广泛;
为了统一缓存的开发规范,j2ee发布了JSR-107缓存规范;
Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry和 Expiry;
• CachingProvider ->定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
• CacheManager ->定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
• Cache ->是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
• Entry ->是一个存储在Cache中的key-value对。
• Expiry -> 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
应用程序想使用缓存:
通过缓存提供者CachingProvider得到缓存管理器CacheManager;
缓存提供者可以管理多个缓存管理器;
每个缓存管理器管理特定类型的缓存;比如:CacheManager1管理redis缓存、CacheManager2管理memcache缓存;
缓存管理器用来管理缓存Cache;
一个缓存管理器管理多个缓存;比如:Cache1用来保存员工数据、Cache2用来缓存部门数据;
缓存中保存多条数据,每一条数据是一个键值对;
Cache接口中提供了一些api,用来对数据进行增删改查操作;
3.spring缓存抽象
Spring也定义了类似JSR-107类似的缓存抽象和缓存注解;
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;
spirng缓存抽象的核心概念和注解:
CacheManager ->缓存管理器,可以指定多种缓存管理器,例如:redis、memcache、concurrentHashMap等等;
Cache ->缓存,一个缓存管理器可以管理多个缓存,缓存中以键值对存放数据;比如:Cache-1专门用来存放员工信息,Cache-2用来存放部门信息等待;
@Cacheable ->标记查询方法,将查询的结果放入缓存;查询时缓存中有数据就不查数据库了;
@CacheEvit ->标记删除方法,会在数据库中删除的同时也清除缓存;
@CachePut ->标记修改方法,会将修改后的数据也更新在缓存上;与@Cacheable不同的是该注解标记的方法即使缓存中有数据也会去查数据库;
注解参数:
注解参数中用到的el表达式:
4.使用spring缓存
1)搭建工程
新建一个web工程,这里使用mysql、数据访问使用mybatis;需要引入相关依赖;
配置yml:
配数据源,以及mybatis的配置文件路径
spring: datasource: url: jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root mybatis: config-location: classpath:mybatis-config.xml #指定全局配置文件的位置 mapper-locations: classpath:com/example/demo/dao/mapper/*.xml #指定sql映射文件的位置
实体类:
public class Employee { private Integer id; private String lastName; private String email; private Integer gender; private Integer 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; } }
数据映射接口:
public interface EmployeeDao { public Employee get(Integer id); public void update(Employee employee); public void delete(Integer id); }
为了能扫描到数据映射接口,需要在工程启动类上加注解:
@MapperScan(value = "com.example.demo.dao")
数据映射xml文件:
如果不知道怎么写,可以去github中搜mybatis,然后看说明文档;
<?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.example.demo.dao.EmployeeDao"> <!--通过id查询--> <select id="get" parameterType="java.lang.Integer" resultType="com.example.demo.entity.Employee"> select * from employee where id = #{id} </select> <!--编辑--> <update id="update" parameterType="com.example.demo.entity.Employee"> UPDATE employee SET `lastName` = #{lastName}, `email` = #{email}, `gender` = #{gender}, `d_id` = #{d_id} WHERE `id` = #{id} </update> <!--删除--> <delete id="delete" parameterType="java.lang.Integer"> DELETE FROM employee WHERE id = #{id} </delete> </mapper>
mybatis全局配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--配置驼峰命名法与数据库命名法的映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
controller:
@RestController public class EmployeeController { @Autowired(required = false) //加required=false,防止报红 private EmployeeDao dao; @RequestMapping("/get/{id}") public Employee get(@PathVariable("id") Integer id){ return dao.get(id); } @RequestMapping("/del/{id}") public void delete(@PathVariable("id") Integer id){ dao.delete(id); } @RequestMapping("/edit") public void edit(@RequestBody Employee emp){ dao.update(emp); } }
踩坑:
如果mybatis的数据映射xml文件放src/main/java目录下时,可能会漏打包;
导致的结果是数据库映射接口绑定错误;
解决:
在pom中的<build>中加一个插件;
<!--防止mybatis的xml不打包--> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>copy-xmls</id> <phase>process-sources</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${basedir}/target/classes</outputDirectory> <resources> <resource> <directory>${basedir}/src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin>
不过启动工程时还是可能会漏打包xml文件;
需要在启动工程前手动点maven的package才行;
为了不这么麻烦,还是直接将数据库映射xml放resource目录下可能会好一点;
浏览器访问:
可以正常访问数据库了
2)使用缓存
工程搭建好后,接下来使用缓存;
在springboot中使用注解式缓存的步骤:
1】 引入spring-boot-starter-cache模块
2】 @EnableCaching开启缓存
3】 使用缓存注解
4】 切换为其他缓存
pom中添加缓存依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
开启基于注解的缓存:
在工程启动类上加注解@EnableCaching
配置yml:
为了调用数据库映射接口时,sql被打印在控制台
logging:
level:
com:
example:
demo:
dao: debug
添加缓存注解:
在数据库映射接口中的查询方法中添加缓存注解@Cacheable
public interface EmployeeDao { @Cacheable(cacheNames = {"emp"}) public Employee get(Integer id); public void update(Employee employee); public void delete(Integer id); }
测试:
请求controller的查询接口;
第一次请求时,控制台会打印sql,说明请求了数据库;
清空控制台,再次请求 :
可以看到没打印sql,说明数据是从缓存中得到的,而不是从数据库;