springCache搭配redis替代mybatis二級緩存[954L]

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執行過程

clientservicedb第一次查詢查詢數據庫數據返回,寫入實現springCache的緩存數據返回第二次查詢命中緩存,返回數據clientservicedb

代碼示例

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!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章