SpringBoot_Cacheable测试

一,基本环境搭建

1,打开IDEA创建项目

项目传经完成

2,检查pop.xml文件需要start是否齐全,我这里报错的maven坐标直接更换了,父项目请使用2.2.1.RELEASE,正确的如下

<?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 https://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.bank.hu</groupId>
    <artifactId>springboot_cache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_cache</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-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3,配置数据源,编写application.properties配置文件,本人使用的是mysql8.0.15,如果使用8之下的版本,请注意驱动名称有变化

spring.datasource.url=jdbc:mysql://localhost:3306/springboot_cache?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=bank@126.com
#mysql8.0之后驱动名改成了com.mysql.cj.jdbc.Driver,这里驱动名注释掉了,让它自动选择
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver

4,创建数据库,创建表,直接粘sql吧,这个都会

CREATE TABLE `department` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `departmentName` VARCHAR(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

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;

数据库表创建后先插入一条数据,后面测试使用

 

5,创建实体类封装数据,Employee.java,Department.java

package com.bank.hu.springboot_cache.bean;

import java.io.Serializable;

public class Employee implements Serializable {
	
	private Integer id;
	private String lastName;
	private String email;
	private Integer gender; //性别 1男  0女
	private Integer dId;
	
	public Employee() {
		super();
	}

	public Employee(Integer id, String lastName, String email, Integer gender, Integer dId) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
		this.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;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", dId="
				+ dId + "]";
	}
	
}
package com.bank.hu.springboot_cache.bean;

public class Department {
	
	private Integer id;
	private String departmentName;
	
	public Department() {
		super();
	}
	public Department(Integer id, String departmentName) {
		super();
		this.id = id;
		this.departmentName = departmentName;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getDepartmentName() {
		return departmentName;
	}
	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}
	@Override
	public String toString() {
		return "Department [id=" + id + ", departmentName=" + departmentName + "]";
	}
}

6,mapper,service层,本案例使用注解版的MyBatis;最后别忘了在主启动类上添加:@MapperScan指定需要扫描的mapper接口所在的包

EmployeeMapper.java

package com.bank.hu.springboot_cache.mapper;

import com.bank.hu.springboot_cache.bean.Employee;
import org.apache.ibatis.annotations.*;

@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},d_id=#{dId} WHERE id=#{id}")
    public void updateEmp(Employee employee);

    @Delete("DELETE FROM employee WHERE id=#{id}")
    public void deleteEmpById(Integer id);

    @Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES(#{lastName},#{email},#{gender},#{dId})")
    public void insertEmployee(Employee employee);

    @Select("SELECT * FROM employee WHERE lastName = #{lastName}")
    Employee getEmpByLastName(String lastName);
}

DepartmentMapper.java

package com.bank.hu.springboot_cache.mapper;

import com.bank.hu.springboot_cache.bean.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface DepartmentMapper {

    @Select("SELECT * FROM department WHERE id = #{id}")
    Department getDeptById(Integer id);
}
package com.bank.hu.springboot_cache.service;

import com.bank.hu.springboot_cache.bean.Employee;
import com.bank.hu.springboot_cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

    
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

   
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        employeeMapper.deleteEmpById(id);
    }

 
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }
}
package com.bank.hu.springboot_cache.service;

import com.bank.hu.springboot_cache.bean.Department;
import com.bank.hu.springboot_cache.mapper.DepartmentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class DeptService {

    @Autowired
    DepartmentMapper departmentMapper;

    public Department getDeptById(Integer id){
        System.out.println("查询部门"+id);
        Department department = departmentMapper.getDeptById(id);
        return department;
    }
}

7,控制层

package com.bank.hu.springboot_cache.controller;

import com.bank.hu.springboot_cache.bean.Employee;
import com.bank.hu.springboot_cache.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;

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping("/emp/{id}")
    public Employee getEmployee(@PathVariable("id") Integer id){
        Employee employee = employeeService.getEmp(id);
        return employee;
    }

    @GetMapping("/emp")
    public Employee update(Employee employee){
        Employee emp = employeeService.updateEmp(employee);

        return emp;
    }

    @GetMapping("/delemp")
    public String deleteEmp(Integer id){
        employeeService.deleteEmp(id);
        return "success";
    }

    @GetMapping("/emp/lastname/{lastName}")
    public Employee getEmpByLastName(@PathVariable("lastName") String lastName){
       return employeeService.getEmpByLastName(lastName);
    }

}
package com.bank.hu.springboot_cache.controller;

import com.bank.hu.springboot_cache.bean.Department;
import com.bank.hu.springboot_cache.service.DeptService;
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
public class DeptController {

    @Autowired
    DeptService deptService;

    @GetMapping("/dept/{id}")
    public Department getDept(@PathVariable("id") Integer id){
        return deptService.getDeptById(id);
    }
}

8,测试启动服务

http://localhost:8080/emp/1

二,开启缓存,在上面的基础上修改

1,在主启动类上添@EnableCaching注解

2,修改service层方法,在方法上添加相应的注解

(1)@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存

 

@Cacheable(value = {"emp"})
public Employee getEmp(Integer id){
       System.out.println("查询"+id+"号员工");
       Employee emp = employeeMapper.getEmpById(id);
       return emp;
}
cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;

@Cacheable(value ={"emp","temp"}
key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值  1-方法的返回值
      编写SpEL; #i d;参数id的值   #a0  #p0  #root.args[0]
      getEmp[2]

keyGenerator:key的生成器;可以自己指定key的生成器的组件id
      key/keyGenerator:二选一使用;

例:@Cacheable(value = {"emp"},keyGenerator = "myKeyGenerator")
cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器

condition:指定符合条件的情况下才缓存;
      ,condition = "#id>0" :id>0时才会缓存
  condition = "#a0>1":第一个参数的值》1的时候才进行缓存
unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
      unless = "#result == null"
      unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
sync:是否使用异步模式

例子:

(1)@Cacheable(cacheNames= {"emp"},key="#root.methodName+'['+#id+']'")

如果key不写,使用默认生成规则

(2)@Cacheable(value = {"emp"},keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2")

keyGenerator和key二选一,自定义keyGenerator ,创建MyCacheConfig.java

package com.bank.hu.springboot_cache.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;

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){

            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString()+"]";
            }
        };
    }
}

(2)@CachePut:既调用方法,又更新缓存数据;同步更新缓存

@CachePut(value = "emp",key = "#result.id")
public Employee updateEmp(Employee employee){
	System.out.println("updateEmp:"+employee);
	employeeMapper.updateEmp(employee);
	return employee;
}

注意:测试之前请注意getEmp(Integer id)方法上的注解,key,condition,unless值

(3)@CacheEvict:缓存清除

key:指定要清除的数据
allEntries = true:指定清除这个缓存中所有的数据
beforeInvocation = false:缓存的清除是否在方法之前执行
    默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除

beforeInvocation = true:
    代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除

@CacheEvict(value="emp",key = "#id")
public void deleteEmp(Integer id){
	System.out.println("deleteEmp:"+id);
	employeeMapper.deleteEmpById(id);
}
(4)@Caching 定义复杂的缓存规则
@Caching(
    cacheable = {
	    @Cacheable(value="emp",key = "#lastName")
    },
    put = {
	    @CachePut(value="emp",key = "#result.id"),
	    @CachePut(value="emp",key = "#result.email")
    }
)
public Employee getEmpByLastName(String lastName){
     return employeeMapper.getEmpByLastName(lastName);
} 

一次查询多个缓存,那个有用哪个,如果都不存在,在调用数据库

(5)方法上公共的注解可以提取到类注解上,添加到类上

@CacheConfig(cacheNames="emp",cacheManager = "employeeCacheManager") //抽取缓存的公共配置

下面方法上就可以不用再写

三,整合Redis

1,pop.xml添加redis的starter

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.application.properties添加redis主机

spring.redis.host=47.102.212.30
spring.redis.port=6379
spring.redis.password=123

如果对redis不了解的请点击下面链接

安装教程:linux安装redis详细教程

使用教程:redis使用手册

3,

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,`@Cacheable`是一个注解,用于声明方法的输出结果应该被缓存起来,以提高应用性能。当你在一个服务方法上使用这个注解,Spring会自动将该方法的返回值放入缓存中,如果后续请求相同的参数,Spring Boot会直接从缓存中获取,而不是再次执行计算。 以下是一个简单的示例代码,演示了如何在Spring Boot控制器方法中使用`@Cacheable`: ```java import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @Controller public class MyController { @Cacheable(value = "myCache", key = "#id") // 定义缓存名称和缓存key生成策略 public String fetchData(@PathVariable Long id) { // 这里是实际的数据获取逻辑,例如数据库查询或远程调用 // 假设我们只是返回id作为示例 return "Data for ID: " + id; } } ``` 在这个例子中: - `value`属性指定了缓存名称("myCache")。 - `key`属性定义了缓存键的生成规则,这里使用`#id`表示直接使用方法参数id作为缓存键。 要测试这样的代码,你可以创建一个单元测试,使用`MockMvc`或者`WebClient`来发送GET请求,并检查缓存是否被正确填充和检索。例如: ```java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc class MyControllerTest { @Autowired private MockMvc mockMvc; @MockBean private YourService yourService; // 如果数据来自其他服务,这里需要mock那个服务 @Test public void testCacheable() throws Exception { // 第一次请求,数据应被计算并存储到缓存 mockMvc.perform(get("/myEndpoint/1")) .andExpect(status().isOk()) .andExpect(content().string("Data for ID: 1")); // 第二次请求,数据应从缓存中获取,而不是再次计算 mockMvc.perform(get("/myEndpoint/1")) .andExpect(status().isOk()) .andExpect(content().string("Data for ID: 1")); // 验证第一次请求调用了yourService,第二次没有 verify(yourService, times(1)).fetchData(1); // 如果是模拟服务,这里验证方法调用次数 } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值