SpringBoot系列——cache快取

  前言

  日常開發中,快取是解決資料庫壓力的一種方案,通常用於頻繁查詢的數據,例如新聞中的熱點新聞,本文記錄springboot中使用cache快取。

 

  官方文檔介紹://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-caching-provider-generic

  

  工程結構

 

 

 

  程式碼編寫

  pom引入依賴,引入cache快取,資料庫使用mysql,ORM框架用jpa

        <!--添加springdata-cache依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!-- 引入ehcache支援 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

        <!--添加springdata-jpa依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--添加MySQL驅動依賴 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

  配置文件

server.port=10010
spring.application.name=springboot-cache

spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:/ehcache.xml

  ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="//ehcache.org/ehcache.xsd" updateCheck="false">

    <!-- 磁碟快取位置 -->
    <diskStore path="java.io.tmpdir"/>

    <!-- maxEntriesLocalHeap:堆記憶體中最大快取對象數,0沒有限制 -->
    <!-- maxElementsInMemory: 在記憶體中快取的element的最大數目。-->
    <!-- eternal:elements是否永久有效,如果為true,timeouts將被忽略,element將永不過期 -->
    <!-- timeToIdleSeconds:發獃秒數,發獃期間未訪問快取立即過期,當eternal為false時,這個屬性才有效,0為不限制 -->
    <!-- timeToLiveSeconds:總存活秒數,當eternal為false時,這個屬性才有效,0為不限制 -->
    <!-- overflowToDisk: 如果記憶體中數據超過記憶體限制,是否要快取到磁碟上 -->
    <!-- statistics:是否收集統計資訊。如果需要監控快取使用情況,應該打開這個選項。默認為關閉(統計會影響性能)。設置statistics="true"開啟統計 -->

    <!--
        默認快取
        無過期時間,但 600 秒內無人訪問快取立即過期
    -->
    <defaultCache
            maxElementsInMemory="1000"
            eternal="false"
            timeToIdleSeconds="600"
            timeToLiveSeconds="0"
            overflowToDisk="false">
    </defaultCache>

    <!--
        xx業務快取
        在有效的 120 秒內,如果連續 60 秒未訪問快取,則快取失效。
        就算有訪問,也只會存活 120 秒。
    -->
    <cache name="myCache"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="0"
           overflowToDisk="false">
    </cache>
</ehcache>

  先寫一個套tb_user表的CRUD程式碼

@RestController
@RequestMapping("/tbUser/")
public class TbUserController {
    @Autowired
    private TbUserService tbUserService;

    //方便測試暫時改成GetMapping
    @GetMapping("list")
//    @PostMapping("list")
    public List<TbUser> list(TbUser entityVo) {
        return tbUserService.list(entityVo);
    }

    @GetMapping("get/{id}")
    public TbUser get(@PathVariable("id")Integer id) {
        return tbUserService.get(id);
    }

    //方便測試暫時改成GetMapping
    @GetMapping("save")
//    @PostMapping("save")
    public TbUser save(TbUser entityVo) {
        return tbUserService.save(entityVo);
    }

    @GetMapping("delete/{id}")
    public Integer delete( @PathVariable("id") Integer id) {
        return tbUserService.delete(id);
    }
}

  opjo實體類要實現序列化

@Entity
@Table(name = "tb_user")
@Data
public class TbUser implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Integer id;//表id

    private String username;//用戶名

    private String password;//密碼

    private Date created;//創建時間

    private Integer descriptionId;//關聯詳情id
}

  serviceImpl中,使用註解來開啟快取

@Service
@Transactional
@CacheConfig(cacheNames = {"myCache"})
public class TbUserServiceImpl implements TbUserService{

    @PersistenceContext
    private EntityManager em;

    @Autowired
    private TbUserRepository tbUserRepository;

    //@Cacheable快取數據:key為userList,value為返回值List<TbUser>
    @Cacheable(key = "'userList'")
    @Override
    public List<TbUser> list(TbUser entityVo) {
        System.out.println("獲取list用戶列錶快取數據,"+new Date());
        return tbUserRepository.findAll(Example.of(entityVo));
    }

    //@Cacheable快取數據:key為參數id,value為返回值TbUser
    @Cacheable(key = "#id")
    @Override
    public TbUser get(Integer id) {
        System.out.println("獲取數據快取,key:"+id);
        Optional<TbUser> optionalE = tbUserRepository.findById(id);
        if (!optionalE.isPresent()) {
            throw new RuntimeException("ID不存在!");
        }
        return optionalE.get();
    }

    //@CachePut快取新增的或更新的數據到快取,其中快取的名稱為people,數據的key是person的id
    @CachePut(key = "#entityVo.id")
    // @CacheEvict從快取中刪除key為參數userList的數據
    @CacheEvict(key = "'userList'")
    @Override
    public TbUser save(TbUser entityVo) {
        System.out.println("新增/更新快取,key:"+entityVo.getId());
        //entityVo傳啥存啥,會全部更新
        return tbUserRepository.save(entityVo);
    }

    //清空所有快取
    @CacheEvict(allEntries=true)
    @Override
    public Integer delete(Integer id) {
        System.out.println("清空所有快取");
        tbUserRepository.deleteById(id);
        return id;
    }
}

 

  效果演示

  //localhost:10010/tbUser/save?id=2&username=李四

  調用save方法,key為2,value為當前tbUser對象的數據被快取下來

  

 

  //localhost:10010/tbUser/get/2

  當我們調用get方法時,直接獲取快取數據,控制台啥也不列印,連serviceImpl的get方法都不進去(可以打斷點調試)

 

 

 

 

 

   

 

  //localhost:10010/tbUser/save?id=2&username=王五

  當我們再次調用save方法更新username時,快取數據也被更新

 

 

 

 

 

 

  //localhost:10010/tbUser/get/2

  再次調用get介面,直接返回快取數據,後台也是方法都不進去,啥也不列印

 

 

 

 

  //localhost:10010/tbUser/delete/2

  調用delete介面,刪除數據,同時刪除快取

 

  再次調用get介面,發現快取數據被清除,查詢資料庫

 

   //localhost:10010/tbUser/list

  首次調用list介面,key為userList的,value為用戶集合數據被快取下來,再次調用直接返回快取數據

 

 

   當調用save介面,數據更新,刪除key為userList的快取,再次調用list時,重新查庫並設置快取

 

 

 

  我們配置了快取發獃時間,當120秒內未使用該快取,立即過期,一直用就會一直存在

  我們先同時訪問兩個介面list、get,list介面2分鐘後再次訪問,get介面不能超過2分鐘是不是訪問一下,結果如預期

 

   PS:原先使用了這個jar包,有報錯

    <dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.8.1</version>
    </dependency> 

  後面改成用上面「程式碼編寫」里pom中引的jnet.sf.ehcache下面的ar

 

  後記

  快取除了能緩解資料庫壓力,還能做用戶登錄狀態控制,例如:用戶登錄成功後cookie中保存頒發的token令牌設置永不過期,快取存活時間也設置永不過期,發獃時間設置1天,這樣只有用戶在1天內有訪問快取介面,那他就可以一直保留登錄狀態,直至有其他業務將token或者快取清掉。

  springboot使用cache快取暫時先記錄到這,後續有空再進行補充。

 

  程式碼開源

 

  程式碼已經開源、託管到我的GitHub、碼云:

  GitHub://github.com/huanzi-qch/springBoot

  碼云://gitee.com/huanzi-qch/springBoot