mybatis_cache系列
建議按順序閱讀,有一些代碼沿用之前的code,與一級緩存完全一致的內容或結果就不再操作了
前言
本文主要闡述springCache的基本使用指南。
上一篇也提到了mybatis二級緩存的弊端,二級緩存作用域有一點是針對xml文件。
假設我們在A.xml緩存了結果集,在B.xml修改了同一條DB數據,則無法影響A.xml中的緩存數據,可能導致緩存與DB數據不一致的問題。
所以本文使用SpringCache替代mybatis的二級緩存,實現緩存數據示例。
springCache介紹
官網入門指南地址:https://spring.io/guides/gs/caching/
springCache實際是一個緩存的抽象,需要我們用具體的實現。比方說用redis。
並且可以在不侵入代碼的前提下實現緩存,例如針對某一個函數設置是否緩存。
SpringCache緩存功能的實現是依靠下面的這幾個註解完成的。
- @EnableCaching:開啓緩存功能
- @Cacheable:定義緩存,用於觸發緩存
- @CachePut:定義更新緩存,觸發緩存更新
- @CacheEvict:定義清除緩存,觸發緩存清除
- @Caching:組合定義多種緩存功能
- @CacheConfig:定義公共設置,位於class之上
Coding
先貼一下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
該註解可添加在函數或類上。
添加在函數上表示這個函數的返回值需要被緩存。
添加在類上表示這個類裏所有的函數的返回值都需要被緩存。
添加@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);
}
}
第一次查詢控制檯打印內容
第二次查詢控制檯打印內容
redis_mananger
根據第一次查詢控制檯的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對象。
@CachePut
用於更新緩存數據,如果本沒有緩存,則創建。
更新同一個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對象中的屬性也是更改之後的。
@CacheEvict
刪除緩存,常用於刪除數據。以及緩存中含有集合數據,修改了其中一條數據時也要添加該註解,否則將出現數據不一致問題。
代碼示例
UserServiceTest
@Test
public void deleteByIdTest(){
userService.deleteById(1);
}
UserService
@CacheEvict(value = "userById")
public void deleteById(Integer id) {
userMapper.deleteById(id);
}
控制檯打印
redis_mananger
sql語句可以看到成功刪除了一條數據,而redis中的緩存數據也同步刪除了。
@Caching
上述的註解基本可以實現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!