建议按顺序阅读,有一些代码沿用之前的code,与一级缓存完全一致的内容或结果就不再操作了
本文主要阐述springCache的基本使用指南。
上一篇也提到了mybatis二级缓存的弊端,二级缓存作用域有一点是针对xml文件。
假设我们在A.xml缓存了结果集,在B.xml修改了同一条DB数据,则无法影响A.xml中的缓存数据,可能导致缓存与DB数据不一致的问题。
所以本文使用SpringCache替代mybatis的二级缓存,实现缓存数据示例。
官网入门指南地址:https://spring.io/guides/gs/caching/
springCache实际是一个缓存的抽象,需要我们用具体的实现。比方说用redis。
并且可以在不侵入代码的前提下实现缓存,例如针对某一个函数设置是否缓存。
SpringCache缓存功能的实现是依靠下面的这几个注解完成的。
先贴一下demo项目结构
项目代码还是沿用之前mybatis一二级缓存的demo示例,没什么代码量,只是加了一个service还有改成了基于springboot的方式。
SpringCacheApplication
package com.w954l.blog;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@EnableCaching
@SpringBootApplication
@MapperScan("com.w954l.blog.mapper")
public class SpringCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCacheApplication.class, args);
}
}
@EnableCaching:开启springCache
application.yml
spring:
application:
name: spring-cache
redis:
# 选择0号数据库
database: 0
# redis服务器 ip
host: cache.954l.com
# redis服务器 端口
port: 6379
# redis服务器 密码
password: password
datasource:
url: jdbc:mysql:///blog_mybatis_cache?characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.jdbc.Driver
cache:
# 配置springCache的实现为redis
type: redis
mybatis:
configuration:
# 打印执行的sql语句到控制台
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.w954l.blog.mapper
关键性代码都加了注释了,就不额外说明了。
该注解可添加在函数或类上。
添加在函数上表示这个函数的返回值需要被缓存。
添加在类上表示这个类里所有的函数的返回值都需要被缓存。
添加@Cacheable注解的service执行过程
代码示例
UserServiceTest
/**
* @author 954L
* @create 2020/6/14 14:36
*/
@SpringBootTest(classes = SpringCacheApplication.class)
class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void queryByIdTest(){
User user = userService.queryById(1);
System.out.println(user);
}
}
UserService
/**
* @author 954L
* @create 2020/6/14 14:35
*/
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Cacheable(value = "userById")
public User queryById(Integer id) {
return userMapper.queryById(id);
}
}
根据第一次查询控制台的sql可以断定执行了sql,再看第二次查询的控制台打印内容并没有打印sql,由此可以说明第二次应该是命中了缓存,导致没有去数据库查询。
打开redis管理工具中确实是缓存了我们刚刚查询的数据。
@Cacheable(value = “userById”)
value:fieldId
存储在redis中是hash数据结构:fieldId -> key -> value
fieldId:就是我们输入的value值:userById
key:默认是所有参数值组合合成作为key,也可自定义,但必须使用SPEL表达式。
如:@Cacheable(value = “userById”, key = “#id”)
value:需要缓存的值,在这里就是指这个函数的返回值,就是User对象。
用于更新缓存数据,如果本没有缓存,则创建。
更新同一个fieldId跟同key下的value内容
代码示例
UserServiceTest
@Test
public void updateByIdTest(){
User user = new User();
user.setId(1);
user.setName("123");
user.setPassword("456");
userService.updateById(user);
queryByIdTest();
}
UserService
@CachePut(value = "userById", key = "#user.id")
public User updateById(User user) {
userMapper.updateById(user);
return user;
}
控制台打印
打印了update语句,却没有打印select,说明select命中了缓存,而打印的User对象中的属性也是更改之后的。
删除缓存,常用于删除数据。以及缓存中含有集合数据,修改了其中一条数据时也要添加该注解,否则将出现数据不一致问题。
代码示例
UserServiceTest
@Test
public void deleteByIdTest(){
userService.deleteById(1);
}
UserService
@CacheEvict(value = "userById")
public void deleteById(Integer id) {
userMapper.deleteById(id);
}
redis_mananger
sql语句可以看到成功删除了一条数据,而redis中的缓存数据也同步删除了。
上述的注解基本可以实现CRUD操作了,最后加一个@Caching吧。
还有其他用法如自定义缓存实现等,以后有空再补上吧。
@Caching主要作用是用于组合多个缓存注解,比方说要把当前返回值数据同时缓存到两个不同的field中。
代码示例
UserServiceTest
@Test
public void queryAllTest(){
List<User> userList = userService.queryAll();
userList.stream().forEach(System.out::print);
}
UserService
@Caching(
cacheable = {
@Cacheable(value = "userAll"),
@Cacheable(value = "users")
},
put = {
@CachePut(value = "userList")
},
evict = {
@CacheEvict(value = "user-list")
}
)
public List<User> queryAll() {
return userMapper.queryAll();
}
redis_mananger
这里就不解释了吧,就是上述几个注解的组合使用,如果都看到这里了,那肯定能明白啥意思。
good Job!