关于Mybaits缓存....
记Mybaits缓存踩的坑
1.问题提出
最近开发一个记录操作前后修改内容的功能,获取修改前数据比较简单,直接从数据库获取,记录修改后的功能也比较简单,直接将用户修改的内容封装成po对象,然后两个比对就可以了,问题就出在这。
2.场景复现
下面是出现问题的代码,简化版(操作分为用户端和管理端):
用户端
public class UserServiceImpl{@Autowiredprivate IUserDao userDao;@Autowiredprivate IUserLogDao dao;@Autowiredprivate StreamAction action;@Override@Transactionalpublic void modifyUser(UserDto dto){Long id = dto.getUserId;//修改前的beanUserBean beforeBean = userDao.queryUser(id);//修改后的beanBeanUtils.copyProperties(dto,beforeBean);//更新和用户有关的内容//....用户信息填充 UserLogBean userLogBean//这里需要修改和用户关联的记录,所以插入一次userDao.insert(userLogBean);//用户端操作流action.request(beforeBean);}
}
- StreamAction.request
@Override
@Transactional
public void request(UserBean userBean){/*** 获取数据库原内容*/UserBean afterBean = userDao.queryUser(id);//比对返回修改内容ModifyBean bean = modify(userBean,afterBean);//...其他 业务//插入修改后的内容userBeanModifyDao.insert(bean);
}
此时去查看数据内添加的内容,可以看到,修改的部分被正确的比对出来了。
管理端
public class UserServiceImpl{@Autowiredprivate IUserDao userDao;@Autowiredprivate IUserLogDao dao;@Autowiredprivate StreamActionManager action;@Override@Transactionalpublic void modifyUser(UserDto dto){Long id = dto.getUserId;//修改前的beanUserBean beforeBean = userDao.queryUser(id);//修改后的beanBeanUtils.copyProperties(dto,beforeBean);//更新和用户有关的内容//....用户信息填充 UserLogBean userLogBean//管理端操作流action.request(beforeBean);}
}
- StreamActionManager.request
@Override
@Transactional
public void request(UserBean userBean){/*** 获取数据库原内容*/UserBean afterBean = userDao.queryUser(id);//比对返回修改内容ModifyBean bean = modify(userBean,afterBean);//...其他 业务//插入修改后的内容userBeanModifyDao.insert(bean);
}
此时我们去数据库查看内容,你就惊奇发现并没有见到修改的内容。不对啊,我们确实已经修改过了,那为什么数据库里不显示呢?
3.发现问题
带着疑问,我们很容易的想到,是不是两个对象内容一模一样?带着疑问,我们尝试着打印一下用户端和管理端,在进行比对前的两个对象内容:
public void request(UserBean userBean){/*** 获取数据库原内容*/UserBean afterBean = userDao.queryUser(id);System.out.println(userBean); System.out.println(afterBean); //比对返回修改内容ModifyBean bean = modify(userBean,afterBean);//......
}
用户端
我们在比对修改内容前打印两个bean,结果如下:
cn.example.core.core.bean.UserBean@5e5a2b74
cn.example.core.core.bean.UserBean@5e5a2b75
两个bean不是同一个对象,符合我们的预期,所以用户端正确入库。
我们再来看管理端
管理端
管理端执行结果
cn.example.core.core.bean.UserBean@5e5a2b77
cn.example.core.core.bean.UserBean@5e5a2b77
!!!oi!!!,我们惊奇的发现,这两个对象是一样的,那就奇了怪了,用户端和管理端业务代码甚至基本都一样的,为什么会造成这个原因呢?我们再输出这两个对象的内容
System.out.println(userBean.toString());
System.out.println(afterBean.toString());
很惊奇的是,这两个对象内容都是修改过的内容,也就是service内通过BeanUtil
s属性赋值过的内容,那我们mysql里的内容去哪了??我们明明还没更新啊!我们赶紧去数据库看一眼,发现数据库里并没更新。数据库里内容没更新,业务里的bean已经被更新过了,这是为什么?
4.排查问题
我们找到出现问题的原因了,是因为管理端两个对象一样。那为什么会一样呢?
我们在业务逻辑里很容易想到类似的场景,比如我们在使用redis的时候,当redis内有数据时,我们希望走redis返回结果而不是走数据库,以提高查询性能,那会不会两个对象一样也是走了缓存呢?我们通过查询数据库我们知道,mysql的查询也会存在缓存,但是按道理来说,我们最后的结果应该是mysql的内容,应该不会是后面的内容。所以只有一种情况,是mybaits的缓存。
5.寻找答案
我们已经确定了是mybaits的缓存导致的问题,但是为什么管理端和用户端还不一样呢?为什么用户端就没有这个问题呢?
我们百度之后发现,mybaits有一、二级缓存之分,二级缓存默认不开启。
哎会不会是用户端的缓存过期了?因为用户端的有一个插入其他表的操作,肯定比管理端慢,对对一定是这个问题,好我们去找度娘,度娘说缓存没有过期时间。好好好这样玩,好好好。
那么问题是什么?我们已经确定不是过期时间的问题了,那我们现在想的就是缓存过期,也就是缓存失效了,我们换个方法去查找内容,“mybatis缓存失效的原因”,我们找到以下结果:
1.不在同一个sqlSession中
2.如果是增删改操作,程序会clear缓存。
3.一级缓存未开启
4.手动清空缓存数据,调用sqlsession.clearCache().
5.更改查询条件
我们一点点往下看:
- 不在同一个sqlSession中
同一个事务内会复用同一个sqlSession。
具体我们查看控制台,可以看到第一个sql执行之前,会有一句话
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3663af34]
在之后的sql执行时会有一句话
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3663af34] from current transaction
都是同一个sqlSession
我们业务层面的代码是在同一个事务里,因为没有设置事物传播机制,虽然有两个事物注解,但是最后都在同一个事务那,可以用
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
查看,会发现事务没有失效且都在一个事物内,显然不是这个原因。
- 如果是增删改操作,程序会clear缓存。
我们用户端涉及的增删改是另外一张表的,排除
- 一级缓存未开启
显然开启了,不然不会有这篇博客
4、5不用看了,都不符合
我们分析完了,仍然没找到原因。那么问题到底在哪呢?
目前最有可能的就是2,但是我们明明更改的是其他表啊,那么不妨,我们就试试改其他表。
@Test
@Transactional //这里的事务一定要加,mybatis的缓存存在条件就是需要有事务,否则你查询会发现两个对象怎么样都不会相同
public void test(){UserBean beforeBean = userDao.queryUser(id);//更新userLogDao.update("1","1");UserBean afterBean = userDao.queryUser(id);System.out.println(beforeBean); System.out.println(afterBean); }
哎,神奇,两个对象不一样了,缓存失效了!所以问题就在这,所以这就是造成这个bug的原因,知道了问题,那我们就开始做解决方案
6.解决方案
解决方案有一下几种:
- 关闭mybatis一级缓存,无法关闭,只能修改状态
mybatis:configuration:cache-enabled: false #禁用二级缓存local-cache-scope: statement #一级缓存指定为statement级别 默认为session级别
- 使用不同的对象传递
在传递前后bean的时候,用其他的bean赋值传递。
- 使用不同的查询方式,拼接条件等
- 使用不同的事物隔离级别,sqlSession是依赖于mysql的事物,所以如果数据库不支持事物那么Spring的事物 也不会生效。我们可以使用不同的事物隔离级别,以创建不同的sqlSessio,此时就不存在bean不同的问题:
@Override@Transactionalpublic void modifyUser(UserDto dto){Long id = dto.getUserId;//修改前的beanUserBean beforeBean = userDao.queryUser(id);//修改后的beanBeanUtils.copyProperties(dto,beforeBean);//更新和用户有关的内容//....用户信息填充 UserLogBean userLogBean//管理端操作流action.request(beforeBean);}//另外一个类的request方法@Override@Transactional(propagation = Propagation.REQUIRES_NEW)public void request(UserBean userBean){/*** 获取数据库原内容*/UserBean afterBean = userDao.queryUser(id);//比对返回修改内容ModifyBean bean = modify(userBean,afterBean);//...其他 业务//插入修改后的内容userBeanModifyDao.insert(bean);}
使用不同的事物传播机制,我们可以看到控制台创建了两个sqlSession:
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7488c183]//....
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4797023d]//再次比较两个bean
cn.example.core.core.bean.UserBean@4b86a656
cn.example.core.core.bean.UserBean@4c3c31a5
到此为止,我们的问题就解决了。
相关文章:
关于Mybaits缓存....
记Mybaits缓存踩的坑 1.问题提出 最近开发一个记录操作前后修改内容的功能,获取修改前数据比较简单,直接从数据库获取,记录修改后的功能也比较简单,直接将用户修改的内容封装成po对象,然后两个比对就可以了ÿ…...
Vue axios调用springboot接口获取数据库数据并显示到网页
axios调用接口获取数据 可以查看简述化的此文 点击 此文简述化文章 PS**由于我自己的本次springboot项目内容很多,所以只是截取了其中关于axios调用接口获取数据的内容,还请大家了解工作原理即可** 前端 添加axios和vue2链接 <script src"htt…...
12-bean创建流程3
文章目录 1 bean实例化前 2. bean实例化doCreateBean() 1 bean实例化前 createBean方法里面的resolveBeforeInstantiation方法,InstantiationAwareBeanPostProcessor接口创建一个代理对象返回 try {// Give BeanPostProcessors a chance to return a p…...
volatile关键字 和 i = i + 1过程
本文是复制粘贴,请直接看原文 原文链接:Java并发编程:volatile关键字解析 - Matrix海子 - 博客园 (cnblogs.com) ------------------------------------------------------------------------------------------------------------------- Java并发编程࿱…...
ubuntu20 安装 cmake 3.27
1. 下载cmake3.27 建议从cmake官网下载安装,虽然比较慢,但从清华镜像里下载的cmake文件不全。 我下载的是:cmake-3.27.7.tar.gz 博客 ubuntu安装cmake的三种方法(超方便!)-CSDN博客 里面提供了三种方法&am…...
faster lio 回环 加入GTSAM优化的记录
首先感谢这位博主的文章:https://blog.csdn.net/weixin_41281151/article/details/125371285,其中部分代码参考于改博主中的github: https://github.com/kahowang/FAST_LIO_SAM 不同的是,我使用的是faster lio进行更改,…...
深入剖析 深度学习中 __init()__函数和forward()函数
目录 前言1. __init()__函数2. forward()函数3. 两者关系 前言 再看代码时,发现init函数和forward函数都有参数,具体是怎么传参的呢? 为了更方便的讲解,会举简单的代码例子结合讲解。 forward() 和 __init__() 是神经网络模型类…...
BUUCTF学习(一):SQL注入,万能密码
1、场景 2、题目 3、解题 用户名:admin or 11# 密码:123456 4、解析SQL注入 “SQL注入是一种常见的Web应用程序漏洞,攻击者可以通过注入的SQL语句获取数据库的敏感信息,对网站用户的数据安全造成威胁。SQL注入的特点包括广泛性、隐…...
基于springboot实现心灵治愈心理健康平台系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现心灵心理健康平台系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个心灵治愈交流平台 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论…...
百度Apollo自动驾驶
百度从2013年开始布局自动驾驶领域,十年来一直坚持压强式的、马拉松式的研发投入,以技术创新驱动长期发展。百度Apollo L4级自动驾驶运营测试里程累计已超5000万公里,拥有自动驾驶专利族超4600件,其中高级别自动驾驶专利族数全球第…...
数据迁移库工具-C版-01-HappySunshineV1.0-(支持Gbase8a)
一、测试环境信息 名称值CPUIntel(R) Core(TM) i5-1035G1 CPU 1.00GHz操作系统CentOS Linux release 7.9.2009 (Core)内存3G逻辑核数2Gbase8a版本8.6.2-R43.34.27468a27HappySunshine版本V1.0 二、支持功能 序号功能1GBASE8a到GBASE8a的库级数据迁移。2批量加载。ÿ…...
【sv】 pack/unpack stream
https://www.amiq.com/consulting/2017/05/29/how-to-pack-data-using-systemverilog-streaming-operators/ https://www.amiq.com/consulting/2017/06/23/how-to-unpack-data-using-the-systemverilog-streaming-operators/...
二、使用DockerCompose部署RocketMQ
使用DockerCompose进行部署 docker-compose的版本为 Docker Compose version v2.2.3 RocketMQ的部署方式以及各自的特点 单master模式 只有一个 master 节点,如果master节点挂掉了,会导致整个服务不可用,线上不宜使用,适合个人学习…...
论文笔记[156]PARAFAC. tutorial and applications
原文下载:https://www.sciencedirect.com/science/article/abs/pii/S0169743997000324 摘要 本文介绍了PARAFAC的多维分解方法及其在化学计量学中的应用。PARAFAC是PCA对高阶数组的推广,但该方法的一些特性与普通的二维情况截然不同。例如,…...
AKKA.Net 的使用 来自CHATGPT
请用C# 语言实现一个自动化设备 流水线调度模型,流水线各个环节需要并行执行: 下面是一个使用C#语言实现自动化设备流水线调度模型的简单示例。该示例使用并发编程库System.Threading.Tasks来实现流水线各个环节的并行执行。 csharp using System; usi…...
网络安全—小白学习笔记
1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高; 二、则是发展相对成熟入…...
OpenRemote: Java 开源 IoT 物联网开发平台,匹配智慧城市、智能家居、能源管理
OpenRemote 是一个直观、用户友好的基于Java语言的开源 IoT 物联网设备管理平台,它包括从连接设备到构建应用程序和特定领域的智能应用程序的所有功能和特性。通过OpenRemote物联网平台,用户可以收集和处理来自不同设备的传感器数据,适用于智…...
GO-unioffice实现word编辑
导包 import ("fmt""log""os""time""github.com/unidoc/unioffice/common/license""github.com/unidoc/unioffice/document" ) 创建word文件 func CreateFile(name string) {filename : name ".docx&quo…...
SpringMVC的拦截器(Interceptor)
拦截器简介 SpringMVC的拦截器Interceptor,主要是对Controller资源访问时进行拦截的基本操作的技术,当然拦截后可以进行权限控制,功能增强等都是可以的。拦截器类似于JavaWeb开发中的Filter,他们之间的区别如下图所示 Filter技术…...
【git】gitlab常用命令
gitlab官网 官网:官网 中文官网:中文官网 默认的gitlab安装目录 /opt/gitlab/bin 启动 gitlab-ctl start 查看状态 gitlab-ctl status 停止 gitlab-ctl stop 重启GitLab gitlab-ctl restart 查看gitlab的配置文件 配置的路径是:/…...
解读下SWD协议以及其应用
SWD协议原理 SWD(Serial Wire Debug)协议是一种用于ARM Cortex微控制器的调试接口协议。它定义了主机计算机与目标设备之间通过SWD线进行通信的格式和规范。 SWD协议使用两根线进行通信:SWDIO(Serial Wire Debug I/O)…...
基于单目的光流法测速
目录 1.简介 2.代码实现 1.简介 基于单目的光流法是一种常见的计算机视觉技术,用于估计图像序列中物体的运动速度。它通过分析连续帧之间的像素变化来推断物体在图像中的移动情况。 背景: 光流法是计算机视觉领域中最早的运动估计方法之一,…...
排序-算法
文章目录 一、排序的概念及引用1.1 排序概念1.2 排序运用1.3 常见排序算法 二、常见排序算法的实现2.1 插入排序2.1.1 基本思想2.1.2 直接插入排序2.1.3 希尔排序 2.2 选择排序2.2.1 基本思想2.2.2 直接选择排序2.2.3 堆排序 2.3 交换排序2.3.1 冒泡排序2.3.2 快速排序2.3.3 快…...
【特纳斯电子】基于单片机的火灾监测报警系统-实物设计
视频及资料链接:基于单片机的火灾监测报警系统-实物设计 - 电子校园网 (mcude.com) 编号: T0152203M-SW 设计简介: 本设计是基于单片机的火灾监测报警系统,主要实现以下功能: 1.通过OLED显示温度、烟雾、是否有火…...
网络安全就业形势怎么样?
泻药,以下都是我本人的肺腑之言,是答主深耕职场多年,转战数家公司总结周围朋友的从业经验才总结出来的行业真相,真心希望帮助到还没有步入职场的大家,尤其是24届的应届毕业生,多掌握些就业信息就能少走一些…...
【Golang】Go的并发和并行性解释。谁说Go不是并行语言?
偶然发现百度上有很多"师出同门"的"go是并发语言,而不是并行语言"的说法。让我顿感奇怪,"并行"说白了就是对CPU多核的利用,这年头不能利用多核的编译语言还有的混?而且还混的这么好?并且…...
k8s-16 k8s调度
调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node上的 Pod。调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。 kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群控制面的一部分如果你真的希望或者有这方面…...
【2023研电赛】全国技术竞赛一等奖:基于FPGA的超低时延激光多媒体终端
该作品参与极术社区组织的研电赛作品征集活动,欢迎同学们投稿,获取作品传播推广,并有丰富礼品哦~ 基于FPGA的超低时延激光多媒体终端 参赛单位:华东师范大学 指导老师:刁盛锡 参赛队员:王泽宇 谢祖炜 秦子淇…...
Annoy vs Milvus:哪个向量数据库更适合您的AI应用?知其然知其所以然
1. Annoy vs Milvus简介 Annoy 和 Milvus 都是用于向量索引和相似度搜索的开源库,它们可以高效地处理大规模的向量数据。 Annoy(Approximate Nearest Neighbors Oh Yeah): Annoy 是一种近似最近邻搜索算法,它通过构…...
android 13.0 SystemUI导航栏添加虚拟按键功能(一)
1.概述 在13.0的系统产品开发中,在系统SystemUI的原生系统中默认只有三键导航,想添加其他虚拟按键就需要先在构建导航栏的相关布局中分析结构,然后添加相关的图标xml就可以了,然后添加对应的点击事件,就可以了,接下来先分析第一步关于导航栏的相关布局情况 然后实现功能 …...
乐陵森森水族/seo数据优化教程
阅读目录 为什么要用xargs,问题的来源xargs是什么,与管道有什么不同xargs的一些有用的选项回到顶部为什么要用xargs,问题的来源 在工作中经常会接触到xargs命令,特别是在别人写的脚本里面也经常会遇到,但是却很容易与管…...
wordpress如何配置opcache/百度网盘app下载
常用的字符串对象一般有如下:String,StringBuffer和StringBuilder 01-创建方式: 1,String类型: 面试官:下面这段代码创建几个常量? String str"hello!"; strstr"world";回答&#…...
网站开发需呀那些技术/揭阳市seo上词外包
[转载]Linux下非root用户如何安装软件这是本人遇到的实际问题,之前用到的所有机器,无论是自己的PC还是云服务器,root权限都是妥妥的,但是现在发现实验室的服务器原来自己并没有root权限2333再看用户的权限。root用户是bug…...
服务器怎样做网站呢/杭州疫情最新情况
参考链接 https://www.cnblogs.com/cxydczzl/archive/2018/08/30/9558294.html...
东莞厚街网站建设/网站排名软件
转自 http://cryinstall.com/?p121 Mr高 被钟大神忽悠装了Opensuse,然后这俩家伙对装系统的引导项设置不对劲,Opensuse的grub直接装到硬盘的MBR上, 然后华丽丽的把原来Fedora 16的grub2给覆盖了,接着Opensuse又识别不了grub2,….…...
phyton 网站开发/宁波网站推广优化公司电话
现实中,我们很多朋友没时间或者不方便去上专门的围棋课,同时又有时间自己在网络上下下棋,就是苦恼于自己水平不高。那么怎么通过自学成为一名围棋高手呢,在此分享一下自己的学习体会。第一步:买书看书。自己买几本围棋…...