spring整合ehcache 注解實現查詢緩存,並實現實時緩存更新或刪除


寫在前面:上一篇博客寫了spring cache和ehcache的基本介紹,個人建議先把這些最基本的知識了解了才能對今天主題有所感觸。不多說了,開干!

注:引入jar

        <!-- 引入ehcache緩存 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.8.3</version>
        </dependency>

 

第一步:首先配置ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">
    
   <diskStore path="java.io.tmpdir"/>    
   <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            overflowToDisk="false" 
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskSpoolBufferSizeMB="30"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
   
    <cache name="myCache"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="30"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
    </cache>   
</ehcache>

第二步:在spring.xml的配置文件中引入schema, 

      xmlns:aop="http://www.springframework.org/schema/aop"和http://www.springframework.org/schema/cache  http://www.springframework.org/schema/cache/spring-cache-3.2.xsd

      緩存的配置:

    <!-- 啟用緩存注解功能,這個是必須的,否則注解不會生效,另外,該注解一定要聲明在spring主配置文件中才會生效 -->
    <cache:annotation-driven cache-manager="ehcacheManager"/>
    
    <!-- cacheManager工廠類,指定ehcache.xml的位置 -->
    <bean id="ehcacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
         <property name="configLocation" value="classpath:ehcache.xml" />
    </bean>
    <!-- 聲明cacheManager -->
    <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
         <property name="cacheManager" ref="ehcacheManagerFactory" />
    </bean>

OK!緩存的相關配置已經完成。下面開始編寫測試程序。這里需要連接數據庫,我就不寫了。這里為了方便就隨便找了之前寫過的model,這個model就是AOP注解實現日志管理的實體,為了偷懶就直接用了,希望你們不要誤解,沒有特殊意義的

第三步:編寫model,這里需要注意,要實現緩存的實體必須要序列化 private static final long serialVersionUID = -6579533328390250520L;  關於序列化的生成這里就不介紹了,大家可以百度看看。

package org.shop.entity;

import java.io.Serializable;
import java.util.Date;

public class SystemLog implements Serializable {
 
    private static final long serialVersionUID = -6579533328390250520L;

    private String id;

    private String description;

    private String method;

    private Long logType;

    private String requestIp;

    private String exceptioncode;

    private String exceptionDetail;

    private String params;

    private String createBy;

    private Date createDate;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id == null ? null : id.trim();
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description == null ? null : description.trim();
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method == null ? null : method.trim();
    }

    public Long getLogType() {
        return logType;
    }

    public void setLogType(Long logType) {
        this.logType = logType;
    }

    public String getRequestIp() {
        return requestIp;
    }

    public void setRequestIp(String requestIp) {
        this.requestIp = requestIp == null ? null : requestIp.trim();
    }

    public String getExceptioncode() {
        return exceptioncode;
    }

    public void setExceptioncode(String exceptioncode) {
        this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim();
    }

    public String getExceptionDetail() {
        return exceptionDetail;
    }

    public void setExceptionDetail(String exceptionDetail) {
        this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim();
    }

    public String getParams() {
        return params;
    }

    public void setParams(String params) {
        this.params = params == null ? null : params.trim();
    }

    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy == null ? null : createBy.trim();
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }
}
View Code

第四步:編寫dao,service

package org.shop.dao;

import org.shop.entity.SystemLog;

public interface SystemLogMapper {
    int deleteByPrimaryKey(String id);

    int insert(SystemLog record);

    int insertSelective(SystemLog record);

    SystemLog selectByPrimaryKey(String id);

    int updateByPrimaryKeySelective(SystemLog record);

    int updateByPrimaryKey(SystemLog record);
    
    int count();
}
View Code
public interface SystemLogService {

    int deleteSystemLog(String id);

    int insert(SystemLog record);
    
    int insertTest(SystemLog record);

    SystemLog findSystemLog(String id);
    
    int updateSystemLog(SystemLog record);
    
    int count();
}
View Code

第五步:編寫serviceImpl並添加緩存注解這里緩存注解的參數不介紹了,不懂得看我上一篇博客,我這里先把需要的注解都寫上了,一會一個一個介紹。

@Service("systemLogService")
public class SystemLogServiceImpl implements SystemLogService {

    @Resource
    private SystemLogMapper systemLogMapper;
    
    @Override
    public int deleteSystemLog(String id) {        
              return systemLogMapper.deleteByPrimaryKey(id);
    }
@Override
//@CachePut(value="myCache") //@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true) @CacheEvict(value="myCache",key="0",beforeInvocation=true) public int insert(SystemLog record) { return systemLogMapper.insertSelective(record); }
@Override @Cacheable(value
="myCache",key="#id") public SystemLog findSystemLog(String id) { return systemLogMapper.selectByPrimaryKey(id); }
@Override
public int updateSystemLog(SystemLog record) { return systemLogMapper.updateByPrimaryKeySelective(record); } @Override public int insertTest(SystemLog record) { return systemLogMapper.insert(record); }
@Override @Cacheable(value
="myCache",key="0") public int count() { int num = systemLogMapper.count(); return num; } }

第六步:編寫controller,即我們的測試。

@Controller
@RequestMapping("systemLogController")
public class SystemLogController {

    @Resource
    private SystemLogService systemLogService;
    
    @RequestMapping("testLog")
    public ModelAndView testLog(){    
        ModelMap modelMap = new ModelMap();
        SystemLog systemLog = systemLogService.findSystemLog("c30e2398-079a-406b-a2f7-a85fa15ccac7");
        modelMap.addAttribute("data", systemLog);
        return new ModelAndView("index",modelMap);
    }
    @RequestMapping("insert")
    @ResponseBody
    public boolean Insert(SystemLog record){
        systemLogService.insert(record);
        return true;
    }
    
    @RequestMapping("test1")
    public ModelAndView test1(){
        ModelMap modelMap = new ModelMap();
        int num =systemLogService.count();
        modelMap.addAttribute("num", num);
        return  new ModelAndView("pageEhcache",modelMap);
    }
    
}

我們先測試查詢的緩存,即serviceImpl中的 findSystemLog(String id) 方法,我們訪問testLog.do,第一次運行如下圖,注意控制台中的heap和 disk

再一次訪問testLog.do,運行你會發現沒有訪問數據庫,如圖:

到此查詢的緩存我們實現了,但是關於緩存的處理我們並沒有做完,我們應該在深入思考下,在上面查詢的緩存生命周期內,我們對剛才查詢的表進行了增刪改操作,這時我們再訪問該查詢方法,你會發現我們的數據並沒有改變,還是增刪改操作之前的數據(因為緩存的生命還在),這里是不是問題呢?此時我們需要對查詢的緩存進行更新或刪除。

下面我們看serviceImpl中的insert方法和count()方法,count的方法是統計表中的數據總記錄,insert方法是對該表進行新增一條記錄,insert的緩存注解用的是@CacheEvict(value="myCache",key="0",beforeInvocation=true),這里清除的是指定緩存,也就是count方法中@Cacheable(value="myCache",key="0")的,(serviceImpl中注釋的@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true)是清除所有的緩存,這里我就不演示了,道理是一樣的)

這里我提供一個測試pageEhcache.jsp頁面,

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>測試</title>  
    <script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.11.1.min.js"></script>
    <script type="text/javascript">
       function insert(){
           var record = $("#formID").serializeArray();
           console.info(record);
           $.ajax({
                   url : "<%=request.getContextPath()%>/systemLogController/insert.do",
                   type : 'post',
                   async:true, 
                   dataType:'json',
                   data : record,             
                   success:function(result){    
                       alert("插入成功!");
                   }
           });           
       }
    </script>
  </head>  
  <body>
  <h1><%=new Date()%></h1>
  <h1>這是一個練習</h1>
     <form id="formID" action="">
        id: <input name="id" type="text"/><br>
        <input type="button" value="插入" onclick="insert()"/>
    </form>
    <br>
    總數:
    <h4>${num}</h4>
  </body>
</html>

 我們先訪問test1.do,看下表中的記錄數並注意控制台變化

頁面顯示如下,注意總數是67

再一次訪問test1.do,沒有訪問數據庫,說明count()方法的緩存生效了,

接下來開始新增記錄,點擊插入按鈕

注意控制台顯示,這里執行了inserSQL語句,並remove了count()方法上的緩存,

接下來再次訪問test1.do,我們看到總數變化了,增加了一條,說明我們把之前count()方法上的緩存刪除了,又執行了查詢總數的sql

再次訪問test1.do,count()方法的緩存生效了,對吧!這個就是@CacheEvict注解的作用。

在insert()方法上還有@CachePut(value="myCache")注解,上面的serviceImpl中注釋了,它的作用是:@CachePut標注的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。

我這里就不做演示了,你們可以自己動手試試。

總結我個人的理解,對查詢方法增加緩存容易,但對於緩存的更新的處理就比較麻煩,我上面的serviceImpl中寫了三種處理方式,

       1.用@CachePut處理,這中方法需要對指定緩存key保持一致,盡管這樣,還是不行,因為它返回的緩存是int(增加或刪除或修改的記錄數或是該記錄的對象,這對我們查詢所有或部分記錄的緩存還是不可行的)

       2.用@CacheEvict(value="myCache",key="0",beforeInvocation=true)處理,清除我們指定key的緩存,這種方式缺點是麻煩,需要我們注意每一個緩存的key

       3.@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true)處理,清除所有緩存,這種方式最省事,但會把其他緩存也一同清除。

隨着業務的復雜性的不斷增加,這些處理方式,可能會增加代碼的復雜性,然后我想到的是對DB層進行緩存,可以利用redis,mamchched的進行處理。當然對於一般的web應用運用ehcache已經刻一解決了,但是對大數據量的運用db級別的緩存效果性能可能會更好。

以上純粹是個人想法。另外我也想了想緩存到底在哪些場景下應用會比較好,不知道你們是怎么認為的。也請大家給點建議。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM