目的:缓存可以通过存储内存中经常访问的数据来减少数据库等底层数据源的压力,从而有效地提高系统的性能和稳定性。
一、启用缓存@EnableCaching在启动类中添加注释@EnableCaching,以打开缓存功能。
示例代码如下:
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication// 打开缓存功能@EnableCachingpublic class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); }}
二、缓存的使用@Cacheable打开缓存后,我们可以使用它@Cacheable、@CachePut、@CacheEvict注释用于缓存。
@Cacheable:该注释可以直接缓存方法运行的结果。在缓存有效期内再次调用该方法时,不会调用该方法本身,而是直接从缓存中获取结果并返回给调用方。
属性名
描述
value/cacheNames
指定缓存的名称
key
缓存数据时key的值,默认使用方法的参数值
keyGenerator
缓存key生成策略(只能和key选一)
cacheManager
指定的缓存管理器(Redis等)
cacheResolver
功能和cachemanager属性一样,只能选择一个
condition
指定的缓存条件 (只有满足才能缓存)
unless
如果符合unless指定的条件,则该方法不会缓存
sync
是否使用异步缓存 默认为false
@Cacheable测试:
1.添加依赖和项目配置文件
<!--jpa--><dependency> <groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency> <!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
# 使用update模式配置数据库Spring.jpa.hibernate.ddl-auto=update# 显示数据执行语句spring.jpa.show-sql=true #Springg数据库配置.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.username=rootspring.datasource.password=123456spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mydb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true
我们使用jpa来创建数据处理层。
2.创建数据实体
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import javax.persistence.*;import java.io.Serializable;/** * @author qx * @date 2023/06/19 * @desc 数据实体 */@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name = "t_student")public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * 姓名 */ @Column private String name; /** * 年龄 */ @Column private Integer age;}
3.创建数据访问层
import com.example.myspringboot.bean.Student;import org.springframework.data.jpa.repository.JpaRepository;public interface StudentRepository extends JpaRepository<Student,Long> {}
4.创建服务层
import com.example.myspringboot.bean.Student;import com.example.myspringboot.repository.StudentRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import java.util.List;/** * @author qx * @date 2023/06/19 * @desc 服务层 */@Servicepublic class StudentService { @Autowired private StudentRepository studentRepository; @Cacheable(value = "student") public List<Student> getAllStudent(){ return studentRepository.findAll(); }}
5.创建测试控制器
import com.example.myspringboot.bean.Student;import com.example.myspringboot.service.StudentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/** * @author qx * @date 2023/06/19 * @desc */@RestController@RequestMapping("/student")public class StudentController { @Autowired private StudentService studentService; @GetMapping("/all") public List<Student> getAllStudent(){ return studentService.getAllStudent(); }}
6.启动项目,访问请求界面
2023-06-19 17:49:45.768 INFO 10012 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'2023-06-19 17:49:45.768 INFO 10012 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'2023-06-19 17:49:45.769 INFO 10012 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 msHibernate: select student0__.id as id1_0_, student0____.age as age2_0_, student0____.name as name3_0_0_0 from t_student student0____
看到控制台打印了sql执行语句。我们再次访问这个接口,并删除浏览器的信息。
继续查询数据,但控制台的SQL语句没有执行,从而从缓存中获取数据。
三、缓存的使用@CachePut@CachePut:在执行前,不会检查缓存中是否有以前执行过的结果,而是每次执行该方法,并将执行结果写入指定的缓存中。通常用于更新缓存。
服务层:
import com.example.myspringboot.bean.Student;import com.example.myspringboot.repository.StudentRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import java.util.Optional;/** * @author qx * @date 2023/06/19 * @desc 服务层 */@Servicepublic class StudentService { @Autowired private StudentRepository studentRepository; @Cacheable(value = "student") public Student getStudent() { return studentRepository.findById(1L).get(); } /** * 修改数据并写入指定的缓存 */ @CachePut(value = "student") public Student updateStudent() { Optional<Student> studentOptional = studentRepository.findById(1L); Student student = studentOptional.get(); student.setName(test2); // 修改数据 studentRepository.save(student); return student; }}
控制层:
import com.example.myspringboot.bean.Student;import com.example.myspringboot.service.StudentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author qx * @date 2023/06/19 * @desc */@RestController@RequestMapping("/student")public class StudentController { @Autowired private StudentService studentService; @GetMapping("/one") public Student getStudent() { return studentService.getStudent(); } @GetMapping("/update") public Student updateStudent() { return studentService.updateStudent(); }}
测试:
2023-06-19 18:08:56.936 INFO 8064 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'2023-06-19 18:08:56.936 INFO 8064 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'2023-06-19 18:08:56.937 INFO 8064 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 msHibernate: select student0__.id as id1_0_0_0_, student0_____.age as age2_0_0_0_, student0_____.name as name3_0_0_0_0 from t_student student0_____ where student0_____.id=?Hibernate: update t_student set age=?Hibernate: update t_student set age=?, name=? where id=?
我们重新查询数据,发现SQL语句没有执行,但更新后的缓存数据被查询。
2023-06-19 18:08:56.936 INFO 8064 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'2023-06-19 18:08:56.936 INFO 8064 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'2023-06-19 18:08:56.937 INFO 8064 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 msHibernate: select student0__.id as id1_0_0_0_, student0_.age as age2_0_0_0_, student0____.name as name3_0_0_0_0 from t_student student0____ where student0____.id=?Hibernate: update t_student set age=?, name=? where id=?
四、缓存的使用@CacheEvict当调用@CacheEvict注解方法时,已存储的数据将从缓存中删除,通常用于删除缓存数据。
属性名
描述
value/cacheNames
缓存的名称
key
缓存的键
allEntries
所有缓存数据是否按缓存名清空,默认为false,当为true时,注释上指定的key属性是否会被忽略
beforeInvocation
方法实施前是否清空缓存? 默认为false
服务层:
import com.example.myspringboot.bean.Student;import com.example.myspringboot.repository.StudentRepository;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.stereotype.Service;import java.util.Optional;/** * @author qx * @date 2023/06/19 * @desc 服务层 */@Servicepublic class StudentService { @Autowired private StudentRepository studentRepository; @Cacheable(value = "student") public Student getStudent() { return studentRepository.findById(1L).get(); } /** * 修改数据并写入指定的缓存 */ @CachePut(value = "student") public Student updateStudent() { Optional<Student> studentOptional = studentRepository.findById(1L); Student student = studentOptional.get(); student.setName(test2); // 修改数据 studentRepository.save(student); return student; } /** * 清除缓存 */ @CacheEvict(value = "student") public void deleteCache() { System.out.println(“清除缓存”); }}
控制层:
import com.example.myspringboot.bean.Student;import com.example.myspringboot.service.StudentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author qx * @date 2023/06/19 * @desc */@RestController@RequestMapping("/student")public class StudentController { @Autowired private StudentService studentService; @GetMapping("/one") public Student getStudent() { return studentService.getStudent(); } @GetMapping("/update") public Student updateStudent() { return studentService.updateStudent(); } @GetMapping("/clearCache") public void clearCache(){ studentService.deleteCache(); }}
在访问查询接口之前,先调用清除缓存的接口。
控制台显示:
2023-06-19 18:22:12.955 INFO 6440 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'2023-06-19 18:22:12.956 INFO 6440 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'2023-06-19 18:22:12.957 INFO 6440 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms清除Hibernateter的缓存: select student0__.id as id1_0_0_0_, student0_____.age as age2_0_0_0_, student0_____.name as name3_0_0_0_0 from t_student student0_____ where student0_____.id=?
从控制台上,我们可以看到SQL语句,在清除缓存后,再次要求查询的接口将重新执行查询。