Springboot + Mybatis + Ehcache

最近在做一个项目,为处理并发性较差的问题,使用了Mybatis二级缓存

但在多表联合查询的情况下,Mybatis二级缓存是存在着数据脏读的问题的

两天就是在想办法解决这个数据脏读的问题

考虑到简易性、性能、兼容性、可扩展性,我选择了springboot自带的 Ehcache 框架来整合解决这个问题

我也是第一次接触到 Ehcache 这个框架,所以也算从零开始

 

根据Mybatis二级缓存的特点,我的思路是

  每当 增删改 某张表 , 就清除刷新其关联的所有表的 缓存

( 这里可分为两个大小方向:

在 增删改 的方法上,做相关的 关联清除缓存 操作  ——  这个就比较细节化一点,做的修改可能会比较多

在 增删改方法 所在类的整体上,做相关的 关联清除缓存操作  —— 这个我需要做的修改就相对少点,但这样里面每个方法(包括一些无关紧要的)都会触发这个清除缓存操作

都各有利弊 )

 

一路上并不顺利,

一开始我尝试自己看能不能找到比较好的方法,但刚接触一下子就自己解决也是不太可能的

但有看到一个解决Mybatis二级缓存问题的插件 —— GitHub上 LuanLouis 的 mybatis-enhanced-cache 

但这个project是5年前写的,而且最近一次更新也只是2年前,现在技术发展那么快,看到的时候就感觉可能不行

后面自己也下载了下来,mvn install 发现不成功,缺少ojdbc14的jar包 ,网上说ojdbc14.jar 是收费的,所以中央库是导不了这个包

看了一些资料说,Oracle安装目录里面有,但公司这台没装Oracle ;也有资料说,可以用 ojdbc6 或其他ojdbc代替,我就下载了ojdbc6 和ojdbc8 都试了一下,都能够解决那个ojdbc jar包的问题,

mybatis-enhanced-cache 插件的jar也成功导入了maven库和项目中,但导入之后,用的时候却又发现报各种错,就觉得凉了,

本来就是为了简单快速,才想用别人的插件,所以我不可能自己又去修改这个插件来用,改了又不一定能用!所以使用插件这个方案就只能放弃了

 

后来,又找了网上很多资料,也按很多示例的步骤做了

像各种注解、各种配置什么的,都不知道是和mybatis的注解冲突了,还是我写的无效

(像网上很多资料说的,mybatis二级缓存是不推荐使用的,至于为什么我还要用,可以看我前面一篇文章——  缓存的设计与使用  ,

所以在方面遇到我这样问题的应该没几个,也就找不到多少资料,只能靠自己)

想了很多可能性,试了一次又一次,想尽量做得简单点,直接简单XML配置加注解 ,能够这样就好了。

但现实是残酷的,做了不知多少次的尝试,还是没成功,无计可施,最后还是只能放弃这个方案了

 

没有办法,就只能选择最后一种方案了 —— 自己写代码,在业务逻辑层 做处理

这对一无所知的菜鸟,也不容易啊

好在坚持尝试,最后终于找到解决方案

配置 ehcache.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!-- <ehcache> -->
 3 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:noNamespaceSchemaLocation="ehcache.xsd">
 5 
 6     <!--
 7         磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存
 8         path:指定在硬盘上存储对象的路径
 9         path可以配置的目录有:
10             user.home(用户的家目录)
11             user.dir(用户当前的工作目录)
12             java.io.tmpdir(默认的临时目录)
13             ehcache.disk.store.dir(ehcache的配置目录)
14             绝对路径(如:d:\\ehcache)
15         查看路径方法:String tmpDir = System.getProperty("java.io.tmpdir");
16      -->
17     <diskStore path="java.io.tmpdir" />
18 
19     <!-- 配置提供者 1、peerDiscovery,提供者方式,有两种方式:自动发现(automatic)、手动配置(manual) 2、rmiUrls,手动方式时提供者的地址,多个的话用|隔开 -->
20     <!-- <cacheManagerPeerProviderFactory
21         class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
22         properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:40002/userCache" /> -->
23     <cacheManagerPeerProviderFactory
24         class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
25         properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446,timeToLive=255"/>
26     <!-- <cacheManagerPeerProviderFactory
27         class="org.ehcache.distribution.RMICacheManagerPeerProviderFactory"
28         properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446,timeToLive=255"/> -->
29 
30     <!-- 配置监听器 1、hostName 主机地址 2、port 端口 3、socketTimeoutMillis socket子模块的超时时间,默认是2000ms -->
31     <!-- <cacheManagerPeerListenerFactory
32         class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
33         properties="hostName=127.0.0.1, port=40001, socketTimeoutMillis=2000" /> -->
34     <cacheManagerPeerListenerFactory
35          class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
36 
37 
38     <!--
39         defaultCache:默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理
40         maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象
41         eternal:代表对象是否永不过期 (指定true则下面两项配置需为0无限期)
42         timeToIdleSeconds:最大的闲置时间 /秒
43         timeToLiveSeconds:最大的存活时间 /秒
44         overflowToDisk:是否允许对象被写入到磁盘
45         说明:下列配置自缓存建立起600秒(10分钟)有效 。
46         在有效的600秒(10分钟)内,如果连续120秒(2分钟)未访问缓存,则缓存失效。
47         就算有访问,也只会存活600秒。
48      -->
49     <defaultCache maxElementsInMemory="10000" eternal="false"
50                   timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="true" />
51 
52     <cache name="*.*.*.*.dao.WarnMapper" maxElementsInMemory="10000" eternal="false"
53                   timeToIdleSeconds="120" timeToLiveSeconds="300" overflowToDisk="true" />
54 
55     <cache name="*.*.*.*.dao.ProjectMapper" maxElementsInMemory="10000" eternal="false" 
56           timeToIdleSeconds="120" timeToLiveSeconds="300" overflowToDisk="true" />
57 </ehcache>

配置 application.properties 

1 # Ehcache缓存
2 spring.cache.type=ehcache
3 spring.cache.ehcache.config=classpath:/ehcache.xml

业务逻辑层代码

 1 package *.*.*.common.utils;
 2 
 3 import net.sf.ehcache.Cache;
 4 import net.sf.ehcache.CacheManager;
 5 
 6 public class EhcacheUtil {
 7 
 8     // 构建一个缓存管理器
 9     private static CacheManager cacheManager = CacheManager.newInstance("src/main/resources/ehcache.xml");  
10     
11     /**
12      * action 清除相关联的缓存
13      * @param cacheName 缓存所在namespace的名称
14      * @param keys   缓存所在namespace下key的名称,为空则默认清空所有key
15      * @return 
16      */
17     public static void clearRelatedCache( String cacheName, String[] keys ) {
18         Cache cache = cacheManager.getCache(cacheName) ;    
19         if ( cache==null ) { 
20             return ;
21         }
22         //若缓存不为空
23         if ( keys==null || keys.length==0 ) {
24             cache.removeAll();
25         }
26         else {
27             for (String key : keys) {
28                 cache.remove(key) ;
29             }            
30         }
31     }
32 
33 }

 

 1    @PostMapping("/delete")
 2     public Result delete(HttpServletRequest request) {
 3         Long projectId = StringUtil.getLong(request.getParameter("id")) ;
 4         Project project = projectService.findById(projectId) ;
 5         if(project==null) {
 6             return ResultGenerator.genFailResult(ResultCode.UNAUTHORIZED, "工程id不正确");
 7         }
 8         projectService.deleteById(projectId);;
 9         EhcacheUtil.clearRelatedCache(CacheConstants.CACHE_NAMESPACE_WARNMAPPER, null) ;
10         return ResultGenerator.genSuccessResult("删除成功");
11     }

 

 

共同学习,共同进步,若有补充,欢迎指出,谢谢!

转载于:https://www.cnblogs.com/dengguangxue/p/11276791.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Autowired注解

package com.how2java.pojo;import org.springframework.beans.factory.annotation.Autowired;public class Product {private int id;private String name;Autowired// 等价于 Resource(name"c")private Category category;public int getId() {return id;}public …

UVa 100

今天也算是练习了一下记忆化搜索&#xff0c;这道题也包含很多值得注意的地方 1、 输入的两个数不一定前者比后者大 2、 变换的过程中可能会超出int范围 3、 为了不超时可以作下预处理 #include<iostream>#include<cstring>#include<cstdio>using namesp…

jquery.cookie.js 使用方法

Cookies 定义&#xff1a;让网站服务器把少量数据储存到客户端的硬盘或内存&#xff0c;从客户端的硬盘读取数据的一种技术&#xff1b; 下载与引入:jquery.cookie.js基于jquery&#xff1b;先引入jquery&#xff0c;再引入&#xff1a;jquery.cookie.js&#xff1b;下载&…

BizTalk开发系列(十二) Schema设计之Group与Order

更多内容请查看&#xff1a;BizTalk动手实验系列目录 BizTalk 开发系列 开发BizTalk项目的时候会先约定各系统之间往来的消息格式. 由于BizTalk内部唯一使用XML文档。因此消息的格式为XML Schema(XML Schema 用于描述 XML 文档的结构)。虽然BizTalk提供了对于XML消息的验证功能…

深入理解JavaScript系列(32):设计模式之观察者模式

介绍 观察者模式又叫发布订阅模式&#xff08;Publish/Subscribe&#xff09;&#xff0c;它定义了一种一对多的关系&#xff0c;让多个观察者对象同时监听某一个主题对象&#xff0c;这个主题对象的状态发生变化时就会通知所有的观察者对象&#xff0c;使得它们能够自动更新自…

JAXB众所周知的秘密

介绍 我重新发现了Java提供给大众的库。 当我第一次阅读该规范时&#xff0c;我很困惑&#xff0c;以为我需要所有这些特殊工具来实现。 我最近发现&#xff0c;只需要一些注释和一个POJO。 杰克斯 JAXB代表XML绑定的Java体系结构。 这种体系结构允许开发人员将来自类的数据转…

题解:CF1914E-Game with Marbles

题解&#xff1a;CF1914E-Game with Marbles 事先说明一下&#xff0c;本题解不讲解简单数据范围的算法&#xff0c;因为复杂数据范围的就很简单。 这道题的大体意思是这样的&#xff1a;小A有颜色为i(i1~n)的小球a[i]个&#xff0c;小B有颜色为i(i1~n)的小球b[i]个。现在他们…

【canvas系列】canvas实现“ 简单的Amaziograph效果”--画对称图【强迫症福利】

标题很难引人入胜&#xff0c;先放个效果图好了 如果图片吸引不了你&#xff0c;那我觉得也就没啥看的了。 demo链接&#xff1a; https://win7killer.github.io/demo_set/html_demo/canvas/can_demo/draw_roll_2.html ************************************************* 上…

原生类型的autoboxing和auto-unboxing

原生类型的autoboxing和auto-unboxing 我们知道&#xff0c;在Java中&#xff0c;int,long等原生类型不是一个继承自Object的类&#xff0c;所以相应的&#xff0c;有很多操作我们都不能利用原生类型操作&#xff0c;比如想要把一个整数放入到一个集合中&#xff0c;我们必须首…

jquery ui datepicker 只能选今天以后的日期

$("input[namebegintime],input[nameendtime],input[nameArrivedate]").datepicker({dateFormat:"yy-mm-dd",minDate: new Date()}); 转载于:https://www.cnblogs.com/y0umer/archive/2012/03/02/3839264.html

全部关于测试–第2部分

这是有关测试系列的第二篇文章。 在第一部分中&#xff0c;我解释了在进行测试开发时需要具备的心态。 或者&#xff0c;换句话说&#xff0c;开发可测试的代码。 在这一部分中&#xff0c;我将介绍一些测试方法的技术。 我将描述的技术可以看作是如何将思维定势转化为行动。 …

简化软件操作,提升用户体验

虽然自己一直都是做网站开发的&#xff0c;没做过什么软件开发&#xff0c;但使用过的软件也不算少。渐渐的发现有些软件真的功能强大&#xff0c;操作性太复杂&#xff0c;于是该软件真的变成了所谓的专业软件&#xff0c;只有经过专业培训的人才能使用。问题是如果不是有很特…

原生js实现简单JSONP

JSONP是一种非常常见的实现跨域请求的方法。其基本思想是利用浏览器中可以跨域请求外链的JS文件&#xff0c;利用这一特性实现数据传输。 用原生JS实现JSONP非常简单&#xff0c;无非几点&#xff1a; 1&#xff09;定义一个函数&#xff0c;用于处理接收到的跨域数据。 2&a…

Java 8流:Micro Katas

编程kata是一种练习&#xff0c;可以帮助程序员通过练习和重复练习来磨练自己的技能。 本文是“ 通过Katas进行Java教程 ”系列的一部分。 本文假定读者已经具有Java的经验&#xff0c;熟悉单元测试的基础知识&#xff0c;并且知道如何从他最喜欢的IDE&#xff08;我是Intelli…

Effective Java第七条:避免使用终结方法

第七条&#xff1a;避免使用终结方法 1&#xff0c; 终结方法&#xff08;finalizer&#xff09;会让程序不稳定&#xff0c;性能降低&#xff0c;出现可移植性的问题。 2&#xff0c; Finalizer在c中师析构器&#xff08;destructors&#xff09;的一部分&#xff0c;用作回…

python3基础:字符串、文本文件

字符串&#xff1a; 练习1&#xff1a; str "大胖三百磅不是二百磅陪着一百磅的小胖" print(str.replace("磅", "斤")) # 替换所有 print(str.replace("磅", "斤", 2)) # 替换两次len len(str) # 这句话的字数长度 pri…

[Python][小知识][NO.3] Python 使用系统默认浏览器打开指定URL的网址

1、前言 一般用到的地方&#xff1a; GUI交互界面下&#xff0c;单击某个按钮实现打开指定网址。 某帮助菜单项目&#xff0c;需要跳转网页显示时。 O.O 某XX程序&#xff0c;需要植入网页弹窗广告时... 2、方法 调用 webbrowser 包中的 open 函数即可。 (没安装该包的 CMD命令…

全部关于测试–第1部分

这是三个系列文章中的第一篇。 测试思路 技术技巧 工具和提示 心态 测试代码是需要学习的东西。 需要花费时间来吸收如何做好。 这是一种应该始终练习和改进的技巧。 过去&#xff0c;开发人员没有进行测试&#xff0c;而是检查了他们的代码。 这是一个很好的技巧&#xf…

利用数据库创建webservice

http://www.cnblogs.com/yungboy/archive/2011/11/07/2239642.html转载于:https://www.cnblogs.com/mingyongcheng/archive/2012/03/08/2385242.html

ASP.NET AJAX Debugging and Tracing

Moved to http://blog.tangcs.com/2008/09/30/asp-net-ajax-debugging-and-tracing/转载于:https://www.cnblogs.com/WarrenTang/archive/2008/09/30/1302328.html