spring整合ehcache注解实现查询缓存,并实现实时缓存更新或删除
转载: http://www.importnew.com/23358.html
写在前面:上一篇博客写了spring cache和ehcache的基本介绍,个人建议先把这些最基本的知识了解了才能对今天主题有所感触。不多说了,开干!
注:引入jar
<!-- 引入ehcache缓存 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.8.3</version> </dependency>
第一步:首先配置ehcache.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:noNamespaceSchemaLocation="ehcache.xsd" 4 updateCheck="true" monitoring="autodetect" 5 dynamicConfig="true"> 6 7 <diskStore path="java.io.tmpdir"/> 8 <defaultCache 9 maxEntriesLocalHeap="10000" 10 eternal="false" 11 overflowToDisk="false" 12 timeToIdleSeconds="120" 13 timeToLiveSeconds="120" 14 diskSpoolBufferSizeMB="30" 15 maxEntriesLocalDisk="10000000" 16 diskExpiryThreadIntervalSeconds="120" 17 memoryStoreEvictionPolicy="LRU"> 18 <persistence strategy="localTempSwap"/> 19 </defaultCache> 20 21 <cache name="myCache" 22 maxEntriesLocalHeap="10000" 23 maxEntriesLocalDisk="1000" 24 eternal="false" 25 diskSpoolBufferSizeMB="30" 26 timeToIdleSeconds="300" 27 timeToLiveSeconds="600" 28 memoryStoreEvictionPolicy="LFU" 29 transactionalMode="off"> 30 <persistence strategy="localTempSwap"/> 31 </cache> 32 </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
缓存的配置:
1 <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 --> 2 <cache:annotation-driven cache-manager="ehcacheManager"/> 3 4 <!-- cacheManager工厂类,指定ehcache.xml的位置 --> 5 <bean id="ehcacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> 6 <property name="configLocation" value="classpath:ehcache.xml" /> 7 </bean> 8 <!-- 声明cacheManager --> 9 <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> 10 <property name="cacheManager" ref="ehcacheManagerFactory" /> 11 </bean>
OK!缓存的相关配置已经完成。下面开始编写测试程序。这里需要连接数据库,我就不写了。这里为了方便就随便找了之前写过的model,这个model就是AOP注解实现日志管理的实体,为了偷懒就直接用了,希望你们不要误解,没有特殊意义的
第三步:编写model,这里需要注意,要实现缓存的实体必须要序列化 private static final long serialVersionUID = -6579533328390250520L; 关于序列化的生成这里就不介绍了,大家可以百度看看。
1 package org.shop.entity; 2 3 import java.io.Serializable; 4 import java.util.Date; 5 6 public class SystemLog implements Serializable { 7 8 private static final long serialVersionUID = -6579533328390250520L; 9 10 private String id; 11 12 private String description; 13 14 private String method; 15 16 private Long logType; 17 18 private String requestIp; 19 20 private String exceptioncode; 21 22 private String exceptionDetail; 23 24 private String params; 25 26 private String createBy; 27 28 private Date createDate; 29 30 public String getId() { 31 return id; 32 } 33 34 public void setId(String id) { 35 this.id = id == null ? null : id.trim(); 36 } 37 38 public String getDescription() { 39 return description; 40 } 41 42 public void setDescription(String description) { 43 this.description = description == null ? null : description.trim(); 44 } 45 46 public String getMethod() { 47 return method; 48 } 49 50 public void setMethod(String method) { 51 this.method = method == null ? null : method.trim(); 52 } 53 54 public Long getLogType() { 55 return logType; 56 } 57 58 public void setLogType(Long logType) { 59 this.logType = logType; 60 } 61 62 public String getRequestIp() { 63 return requestIp; 64 } 65 66 public void setRequestIp(String requestIp) { 67 this.requestIp = requestIp == null ? null : requestIp.trim(); 68 } 69 70 public String getExceptioncode() { 71 return exceptioncode; 72 } 73 74 public void setExceptioncode(String exceptioncode) { 75 this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim(); 76 } 77 78 public String getExceptionDetail() { 79 return exceptionDetail; 80 } 81 82 public void setExceptionDetail(String exceptionDetail) { 83 this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim(); 84 } 85 86 public String getParams() { 87 return params; 88 } 89 90 public void setParams(String params) { 91 this.params = params == null ? null : params.trim(); 92 } 93 94 public String getCreateBy() { 95 return createBy; 96 } 97 98 public void setCreateBy(String createBy) { 99 this.createBy = createBy == null ? null : createBy.trim(); 100 } 101 102 public Date getCreateDate() { 103 return createDate; 104 } 105 106 public void setCreateDate(Date createDate) { 107 this.createDate = createDate; 108 } 109 }
第四步:编写dao,service
1 package org.shop.dao; 2 3 import org.shop.entity.SystemLog; 4 5 public interface SystemLogMapper { 6 int deleteByPrimaryKey(String id); 7 8 int insert(SystemLog record); 9 10 int insertSelective(SystemLog record); 11 12 SystemLog selectByPrimaryKey(String id); 13 14 int updateByPrimaryKeySelective(SystemLog record); 15 16 int updateByPrimaryKey(SystemLog record); 17 18 int count(); 19 }
1 public interface SystemLogService { 2 3 int deleteSystemLog(String id); 4 5 int insert(SystemLog record); 6 7 int insertTest(SystemLog record); 8 9 SystemLog findSystemLog(String id); 10 11 int updateSystemLog(SystemLog record); 12 13 int count(); 14 }
第五步:编写serviceImpl并添加缓存注解。这里缓存注解的参数不介绍了,不懂得看我上一篇博客,我这里先把需要的注解都写上了,一会一个一个介绍。
1 @Service("systemLogService") 2 public class SystemLogServiceImpl implements SystemLogService { 3 4 @Resource 5 private SystemLogMapper systemLogMapper; 6 7 @Override 8 public int deleteSystemLog(String id) { 9 return systemLogMapper.deleteByPrimaryKey(id); 10 } 11 12 @Override 13 //@CachePut(value="myCache") 14 //@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true) 15 @CacheEvict(value="myCache",key="0",beforeInvocation=true) 16 public int insert(SystemLog record) { 17 return systemLogMapper.insertSelective(record); 18 } 19 20 @Override 21 @Cacheable(value="myCache",key="#id") 22 public SystemLog findSystemLog(String id) { 23 return systemLogMapper.selectByPrimaryKey(id); 24 } 25 26 @Override 27 public int updateSystemLog(SystemLog record) { 28 return systemLogMapper.updateByPrimaryKeySelective(record); 29 } 30 @Override 31 public int insertTest(SystemLog record) { 32 return systemLogMapper.insert(record); 33 } 34 35 @Override 36 @Cacheable(value="myCache",key="0") 37 public int count() { 38 int num = systemLogMapper.count(); 39 return num; 40 } 41 42 }
第六步:编写controller,即我们的测试。
1 @Controller 2 @RequestMapping("systemLogController") 3 public class SystemLogController { 4 5 @Resource 6 private SystemLogService systemLogService; 7 8 @RequestMapping("testLog") 9 public ModelAndView testLog(){ 10 ModelMap modelMap = new ModelMap(); 11 SystemLog systemLog = systemLogService.findSystemLog("c30e2398-079a-406b-a2f7-a85fa15ccac7"); 12 modelMap.addAttribute("data", systemLog); 13 return new ModelAndView("index",modelMap); 14 } 15 @RequestMapping("insert") 16 @ResponseBody 17 public boolean Insert(SystemLog record){ 18 systemLogService.insert(record); 19 return true; 20 } 21 22 @RequestMapping("test1") 23 public ModelAndView test1(){ 24 ModelMap modelMap = new ModelMap(); 25 int num =systemLogService.count(); 26 modelMap.addAttribute("num", num); 27 return new ModelAndView("pageEhcache",modelMap); 28 } 29 30 }
我们先测试查询的缓存,即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页面,
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <% 3 String path = request.getContextPath(); 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 5 %> 6 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 7 <html> 8 <head> 9 <title>测试</title> 10 <script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.11.1.min.js"></script> 11 <script type="text/javascript"> 12 function insert(){ 13 var record = $("#formID").serializeArray(); 14 console.info(record); 15 $.ajax({ 16 url : "<%=request.getContextPath()%>/systemLogController/insert.do", 17 type : 'post', 18 async:true, 19 dataType:'json', 20 data : record, 21 success:function(result){ 22 alert("插入成功!"); 23 } 24 }); 25 } 26 </script> 27 </head> 28 <body> 29 <h1><%=new Date()%></h1> 30 <h1>这是一个练习</h1> 31 <form id="formID" action=""> 32 id: <input name="id" type="text"/> 33 <input type="button" value="插入" onclick="insert()"/> 34 </form> 35 36 总数: 37 <h4>${num}</h4> 38 </body> 39 </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中写了三种处理方式,
- 用@CachePut处理,这中方法需要对指定缓存key保持一致,尽管这样,还是不行,因为它返回的缓存是int(增加或删除或修改的记录数或是该记录的对象,这对我们查询所有或部分记录的缓存还是不可行的)
- 用@CacheEvict(value=”myCache”,key=”0″,beforeInvocation=true)处理,清除我们指定key的缓存,这种方式缺点是麻烦,需要我们注意每一个缓存的key
- 用@CacheEvict(value=”myCache”,allEntries=true,beforeInvocation=true)处理,清除所有缓存,这种方式最省事,但会把其他缓存也一同清除。
随着业务的复杂性的不断增加,这些处理方式,可能会增加代码的复杂性,然后我想到的是对DB层进行缓存,可以利用redis,mamchched的进行处理。当然对于一般的web应用运用ehcache已经可以解决了,但是对大数据量的运用db级别的缓存效果性能可能会更好。
♥ 作者:明志健致远
♠ 出处:http://www.cnblogs.com/study-everyday/
♦ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
♣ 本博客大多为学习笔记或读书笔记,本文如对您有帮助,还请多推荐下此文,如有错误欢迎指正,相互学习,共同进步。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?