1、为什么使用缓存
我们都知道,一个程序的瓶颈通常都在数据库,很多场景需要获取相同的数据。比如网站页面数据等,
需要一次次的请求数据库,导致大部分时间都浪费在数据库查询和方法调用上,这时就可以利用到缓存来缓解这个问题。
2、声明式缓存
Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。
在使用 Spring 集成 Cache 的时候,我们需要注册实现的 CacheManager 的 Bean。
Spring Boot 为我们自动配置了 JcacheCacheConfiguration、 EhCacheCacheConfiguration、HazelcastCacheConfiguration、
GuavaCacheConfiguration、RedisCacheConfiguration、SimpleCacheConfiguration 等。
3、默认使用 ConcurrenMapCacheManager
在我们不使用其他第三方缓存依赖的时候,springboot自动采用ConcurrenMapCacheManager作为缓存管理器。
4、使用过程需要注意什么
1、明确哪些方法需要使用缓存,正确使用使得程序变得高效,不会导致编码累赘
2、用于解决哪些实际问题
5、下面是一个简单的例子,只截图了关键使用部分,springboot+mybatis+mysql+spring cahce实现的缓存效果
依赖
<!-- mysql连接类-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--alibaba连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<!-- 开启web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- cache缓存环境-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--springboot整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
properties.yml配置 cache无需配置,只配置了mysql#mysql数据库连接
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/hotel?serverTimezone=UTC
username: root
password: aaaaaa
开启缓存功能,需要正在启动类上加注解@EnableCaching
@SpringBootApplication
//开启缓存
@EnableCaching
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
此处实体类、dao层、service层省略了,只展示controller部分代码,因为该cache功能主要是用注解来实现缓存效果
package com.hotel.demo.web;
import com.hotel.demo.bean.Style;
import com.hotel.demo.dao.JpaStyleDao;
import com.hotel.demo.service.StyleService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.transaction.Transactional;
import java.util.List;
import java.util.jar.JarEntry;
@RestController
@RequestMapping("/style")
@Transactional
public class StyleController {
private static final Logger logger = LoggerFactory.getLogger(StyleController.class);
@Autowired
StyleService styleService;
@Autowired
JpaStyleDao jpaStyleDao;
/*插入数据*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ApiOperation(value = "添加房间类型", notes = "添加房间类型")
@CachePut(value = "style")
public int add() throws RuntimeException {
Style style = new Style();
style.setStyle("大房");
style.setMoney(125.0);
style.setFacilities("卫生间、健生房");
style.setMessage("可住两人");
return styleService.add(style);
}
//更新数据
/*@PathVariable@RequestParam 一起使用 请求方式http://127.0.0.1:8080/style/update/5?style=大房*/
@RequestMapping(value = "/update/{id}", method = RequestMethod.PUT)
@ApiOperation(value = "更新数据", notes = "更新数据")
@ApiImplicitParams({
@ApiImplicitParam(name = "style", value = "类型名", required = true, dataType = "String", paramType = "path"),
@ApiImplicitParam(name = "id", value = "类型id", required = true, dataType = "int", paramType = "path")
})
@CachePut(value="style",key="#id")
public int update(@PathVariable int id, @RequestParam(value = "style", required = true) String style) {
/* Style style = new Style();
style.setId(id);
style.setStyle(styles);
style.setMoney(126.0);
style.setFacilities("卫生间、健生房");
style.setMessage("可住两人");
return styleService.update(style);*/
return styleService.update(style, id);
}
//删除数据
@ApiOperation(value = "删除数据", notes = "删除数据")
@ApiImplicitParam(name = "id", value = "类型ID", required = true, dataType = "int", paramType = "path")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
@CacheEvict(value="style",key="#id")
public void delete(@PathVariable int id) {
styleService.delete(id);
}
//按照ID查找数据
@ApiOperation(value = "按照ID查找数据", notes = "按照ID查找数据")
@ApiImplicitParam(name = "id", value = "类型ID", required = true, dataType = "int", paramType = "path")
@RequestMapping(value = "/findById/{id}", method = RequestMethod.GET)
//打开该方法的缓存
@Cacheable(value = "style", key = "#id")
public Style findById(@PathVariable int id) {
long startTime = System.currentTimeMillis();
Style style = styleService.findById(id);
long endTime = System.currentTimeMillis();
logger.info("start -->end时间差" + (endTime - startTime));
return style;
}
//查找所有数据
@ApiIgnore//使用该注解忽略这个API
@ApiOperation(value = "查找所有数据", notes = "查找所有数据")
@RequestMapping(value = "/findAll", method = RequestMethod.GET)
@Cacheable(value = "emp")
public List<Style> findAll() {
return styleService.findAll();
}
}
大家还需要关注类方法上的注解@Cacheable()、 @CacheEvict()、@CachePut()
解释测试方法
1.add方法
方法中使用到了@CachePut注解,这个注解直接将返回值放入缓存中,通常用于保存和修改方法中
2.findAll、findById方法
方法中使用到了@Cacheable注解,这个注解在执行前先查看缓存中是不是已经存在了,如果存在,直接返回。如果不存在,将方法的返回值放入缓存。
3.delete方法
方法中使用到了@CacheEvict注解,这个注解在执行方法执行成功后会将操作的对象从缓存中移除,仅针对某一次的缓存
@CacheEvict也可以将所有缓存删除,写法为 @CacheEvict(value = “house”, allEntries = true) allEntries不写默认为false 只删除注解方法指定的缓存
参数解释
如@Cacheable(value = “style”, key = “#id”)中value指的是给该次缓存取一个名字自定义,key为该缓存的键
1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如
@Cacheable(value = "emp" ,key = "targetClass + methodName +#p0")
public List<NewJob> queryAll(User uid) {
return newJobDao.findAllByUid(uid);
}
2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
public void delete(@PathVariable int id) {
styleService.delete(id);
}
或
@Cacheable(value="users", key="#p0")
public void delete(@PathVariable int id) {
styleService.delete(id);
}
注意
这个缓存注解对的意义是当查询有缓存存在时,直接返回缓存值,而不会再去执行注解的方法,
参考findById方法的时间差打印,执行两次findById,只会执行一次打印,第二次执行直接返回,没有执行该方法
如果大家想让缓存效果更明显一点,可以使用springboot+jpa+mybatis+cache,需要开启show-sql: true 打印sql语句
在执行的时候你会发现,当你执行第一次查询时会打印sql,第二次就不会打印;