当前位置: 首页 > news >正文

关于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内通过BeanUtils属性赋值过的内容,那我们mysql里的内容去哪了??我们明明还没更新啊!我们赶紧去数据库看一眼,发现数据库里并没更新。数据库里内容没更新,业务里的bean已经被更新过了,这是为什么?

4.排查问题

我们找到出现问题的原因了,是因为管理端两个对象一样。那为什么会一样呢?

我们在业务逻辑里很容易想到类似的场景,比如我们在使用redis的时候,当redis内有数据时,我们希望走redis返回结果而不是走数据库,以提高查询性能,那会不会两个对象一样也是走了缓存呢?我们通过查询数据库我们知道,mysql的查询也会存在缓存,但是按道理来说,我们最后的结果应该是mysql的内容,应该不会是后面的内容。所以只有一种情况,是mybaits的缓存

5.寻找答案

我们已经确定了是mybaits的缓存导致的问题,但是为什么管理端和用户端还不一样呢?为什么用户端就没有这个问题呢?

我们百度之后发现,mybaits有一、二级缓存之分,二级缓存默认不开启。

哎会不会是用户端的缓存过期了?因为用户端的有一个插入其他表的操作,肯定比管理端慢,对对一定是这个问题,好我们去找度娘,度娘说缓存没有过期时间。好好好这样玩,好好好。

那么问题是什么?我们已经确定不是过期时间的问题了,那我们现在想的就是缓存过期,也就是缓存失效了,我们换个方法去查找内容,“mybatis缓存失效的原因”,我们找到以下结果:

1.不在同一个sqlSession中
2.如果是增删改操作,程序会clear缓存。
3.一级缓存未开启
4.手动清空缓存数据,调用sqlsession.clearCache().
5.更改查询条件

我们一点点往下看:

  1. 不在同一个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();

查看,会发现事务没有失效且都在一个事物内,显然不是这个原因。

  1. 如果是增删改操作,程序会clear缓存。

我们用户端涉及的增删改是另外一张表的,排除

  1. 一级缓存未开启

显然开启了,不然不会有这篇博客

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对象,然后两个比对就可以了&#xff…...

Vue axios调用springboot接口获取数据库数据并显示到网页

axios调用接口获取数据 可以查看简述化的此文 点击 此文简述化文章 PS**由于我自己的本次springboot项目内容很多&#xff0c;所以只是截取了其中关于axios调用接口获取数据的内容&#xff0c;还请大家了解工作原理即可** 前端 添加axios和vue2链接 <script src"htt…...

12-bean创建流程3

文章目录 1 bean实例化前 2. bean实例化doCreateBean&#xff08;&#xff09; 1 bean实例化前 createBean方法里面的resolveBeforeInstantiation方法,InstantiationAwareBeanPostProcessor接口创建一个代理对象返回 try {// Give BeanPostProcessors a chance to return a p…...

volatile关键字 和 i = i + 1过程

本文是复制粘贴,请直接看原文 原文链接:Java并发编程&#xff1a;volatile关键字解析 - Matrix海子 - 博客园 (cnblogs.com) ------------------------------------------------------------------------------------------------------------------- Java并发编程&#xff1…...

ubuntu20 安装 cmake 3.27

1. 下载cmake3.27 建议从cmake官网下载安装&#xff0c;虽然比较慢&#xff0c;但从清华镜像里下载的cmake文件不全。 我下载的是&#xff1a;cmake-3.27.7.tar.gz 博客 ubuntu安装cmake的三种方法&#xff08;超方便&#xff01;&#xff09;-CSDN博客 里面提供了三种方法&am…...

faster lio 回环 加入GTSAM优化的记录

首先感谢这位博主的文章&#xff1a;https://blog.csdn.net/weixin_41281151/article/details/125371285&#xff0c;其中部分代码参考于改博主中的github&#xff1a; https://github.com/kahowang/FAST_LIO_SAM 不同的是&#xff0c;我使用的是faster lio进行更改&#xff0c…...

深入剖析 深度学习中 __init()__函数和forward()函数

目录 前言1. __init()__函数2. forward()函数3. 两者关系 前言 再看代码时&#xff0c;发现init函数和forward函数都有参数&#xff0c;具体是怎么传参的呢&#xff1f; 为了更方便的讲解&#xff0c;会举简单的代码例子结合讲解。 forward() 和 __init__() 是神经网络模型类…...

BUUCTF学习(一):SQL注入,万能密码

1、场景 2、题目 3、解题 用户名&#xff1a;admin or 11# 密码&#xff1a;123456 4、解析SQL注入 “SQL注入是一种常见的Web应用程序漏洞&#xff0c;攻击者可以通过注入的SQL语句获取数据库的敏感信息&#xff0c;对网站用户的数据安全造成威胁。SQL注入的特点包括广泛性、隐…...

基于springboot实现心灵治愈心理健康平台系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现心灵心理健康平台系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个心灵治愈交流平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论…...

百度Apollo自动驾驶

百度从2013年开始布局自动驾驶领域&#xff0c;十年来一直坚持压强式的、马拉松式的研发投入&#xff0c;以技术创新驱动长期发展。百度Apollo L4级自动驾驶运营测试里程累计已超5000万公里&#xff0c;拥有自动驾驶专利族超4600件&#xff0c;其中高级别自动驾驶专利族数全球第…...

数据迁移库工具-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批量加载。&#xff…...

【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 节点&#xff0c;如果master节点挂掉了&#xff0c;会导致整个服务不可用&#xff0c;线上不宜使用&#xff0c;适合个人学习…...

论文笔记[156]PARAFAC. tutorial and applications

原文下载&#xff1a;https://www.sciencedirect.com/science/article/abs/pii/S0169743997000324 摘要 本文介绍了PARAFAC的多维分解方法及其在化学计量学中的应用。PARAFAC是PCA对高阶数组的推广&#xff0c;但该方法的一些特性与普通的二维情况截然不同。例如&#xff0c;…...

AKKA.Net 的使用 来自CHATGPT

请用C# 语言实现一个自动化设备 流水线调度模型&#xff0c;流水线各个环节需要并行执行&#xff1a; 下面是一个使用C#语言实现自动化设备流水线调度模型的简单示例。该示例使用并发编程库System.Threading.Tasks来实现流水线各个环节的并行执行。 csharp using System; usi…...

网络安全—小白学习笔记

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟入…...

OpenRemote: Java 开源 IoT 物联网开发平台,匹配智慧城市、智能家居、能源管理

OpenRemote 是一个直观、用户友好的基于Java语言的开源 IoT 物联网设备管理平台&#xff0c;它包括从连接设备到构建应用程序和特定领域的智能应用程序的所有功能和特性。通过OpenRemote物联网平台&#xff0c;用户可以收集和处理来自不同设备的传感器数据&#xff0c;适用于智…...

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&#xff0c;主要是对Controller资源访问时进行拦截的基本操作的技术&#xff0c;当然拦截后可以进行权限控制&#xff0c;功能增强等都是可以的。拦截器类似于JavaWeb开发中的Filter&#xff0c;他们之间的区别如下图所示 Filter技术…...

【git】gitlab常用命令

gitlab官网 官网&#xff1a;官网 中文官网&#xff1a;中文官网 默认的gitlab安装目录 /opt/gitlab/bin 启动 gitlab-ctl start 查看状态 gitlab-ctl status 停止 gitlab-ctl stop 重启GitLab gitlab-ctl restart 查看gitlab的配置文件 配置的路径是&#xff1a;/…...

ESP32硬件定时器虚拟化:16路ISR定时器实现原理与工程实践

1. ESP32_New_TimerInterrupt 库深度解析&#xff1a;16路高精度硬件定时器中断的工程实践1.1 为什么嵌入式系统迫切需要此库在ESP32系列微控制器的实际工程开发中&#xff0c;硬件定时器资源极其稀缺且关键。标准ESP32芯片仅配备两组定时器组&#xff08;Timer Group 0/1&…...

用51单片机+无源蜂鸣器播放《两只老虎》完整教程(附代码与乐理速成)

用51单片机驱动无源蜂鸣器演奏《两只老虎》全流程解析 第一次听到单片机播放音乐时&#xff0c;那种"机器唱歌"的奇妙感至今难忘。作为电子爱好者入门必备的趣味项目&#xff0c;用蜂鸣器演奏音乐不仅能巩固定时器、中断等核心知识&#xff0c;更能将枯燥的理论转化为…...

抖音视频批量下载器:如何快速高效地收集和管理海量抖音内容

抖音视频批量下载器&#xff1a;如何快速高效地收集和管理海量抖音内容 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 抖音作为国内最大的短视频平台&#xff0c;每天产生数以百万计的视频内容&#xff0c;…...

Agent 性能优化:降低 Token 消耗的 5 个技巧

Agent 性能优化&#xff1a;降低 Token 消耗的 5 个技巧系列文章&#xff1a; 《AI Agent 开发实战》第 7 期 难度等级&#xff1a; ⭐⭐⭐⭐ 预计耗时&#xff1a; 35 分钟&#x1f3af; 本文目标 学会优化 AI Agent 性能&#xff1a; ✅ 减少 Token 消耗✅ 提高响应速度✅ 降…...

ABC系统实战指南:逻辑综合与形式验证的数字电路设计工具

ABC系统实战指南&#xff1a;逻辑综合与形式验证的数字电路设计工具 【免费下载链接】abc ABC: System for Sequential Logic Synthesis and Formal Verification 项目地址: https://gitcode.com/gh_mirrors/ab/abc 在现代数字电路设计流程中&#xff0c;逻辑综合与形式…...

深入解析FOC电机控制:从理论到实践的无传感器实现

1. 无传感器FOC控制的核心原理 磁场定向控制&#xff08;FOC&#xff09;本质上是在模拟直流电机的控制方式。想象一下小时候玩的四驱车——直流电机通过改变电压就能直接控制转速&#xff0c;简单粗暴。但三相交流电机就像个傲娇的艺术家&#xff0c;需要我们把三相电流"…...

Modelsim仿真Objects窗口一片空白?别急着重装,试试这个被忽略的优化选项设置

Modelsim仿真Objects窗口空白问题深度排查指南 当你在Modelsim中精心搭建的仿真环境突然"失明"——Objects窗口一片空白&#xff0c;而代码明明编译通过时&#xff0c;这种看似无解的困境往往让工程师陷入重装软件的冲动。但请先别急着点击卸载按钮&#xff0c;这很可…...

PFC颗粒流代码模拟岩石预制裂隙与完整岩石单轴压缩对比分析

PFC颗粒流代码 pfc离散元岩石预制裂隙&#xff0c;裂隙岩石与完整岩石单轴压缩代码&#xff0c;可出各种裂隙形式&#xff0c;可分析应力应变曲线图&#xff0c;裂隙发育与数量&#xff0c;能量变化&#xff0c;简易声发射分析等做岩石单轴压缩离散元模拟的&#xff0c;谁没为…...

华为MatePad 11鸿蒙2.0平板变身编程本:保姆级AidLux+VSCode配置避坑指南

华为MatePad 11鸿蒙平板编程环境搭建实战&#xff1a;AidLux与VSCode高效配置指南 在移动办公与碎片化学习成为主流的今天&#xff0c;将华为MatePad 11这样的高性能平板转变为便携式编程工作站&#xff0c;正成为越来越多开发者的现实需求。鸿蒙系统2.0的分布式能力与AidLux的…...

如何一键完成飞书文档格式转换:3种高效迁移方法指南

如何一键完成飞书文档格式转换&#xff1a;3种高效迁移方法指南 【免费下载链接】feishu2md 一键命令下载飞书文档为 Markdown 项目地址: https://gitcode.com/gh_mirrors/fe/feishu2md 想要将飞书文档快速转换为Markdown格式吗&#xff1f;feishu2md项目为您提供了一键…...