当前位置: 首页 > 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赋值传递。

  • 使用不同的查询方式,拼接条件等
  • 使用不同的事物隔离级别:
//todo 

到此为止,我们的问题就解决了。

相关文章:

Mybaits缓存踩的坑

记Mybaits缓存踩的坑 1.问题提出 最近开发一个记录操作前后修改内容的功能,获取修改前数据比较简单,直接从数据库获取,记录修改后的功能也比较简单,直接将用户修改的内容封装成po对象,然后两个比对就可以了&#xff…...

全国工商注册数据库的作用

随着经济的发展和市场竞争的加剧,越来越多的人开始关注公司的工商信息。这些信息不仅可以帮助人们了解公司的基本情况,还可以为投资者、合作伙伴、员工等提供决策依据。 工商数据库提供了全国范围内企业的基本信息。这些信息包括企业的名称、统一社会信用…...

【Linux】NTP时间服务器Chrony配置详解

🍁 博主 "开着拖拉机回家"带您 Go to New World.✨🍁 🦄 个人主页——🎐开着拖拉机回家_大数据运维-CSDN博客 🎐✨🍁 🪁🍁 希望本文能够给您带来一定的帮助🌸文…...

今年的秋招面试,确实有点难。

不可否认的是,今年秋招确实有点难 从今年的形势来看,好的 offer 都掌握在少数人的手里,想要秋招找到理想的工作,要么学历好,要么技术功底很扎实,这两样都不占的话,就业压力就会比较大。 如何从…...

Rn使用FlatList导航栏自动回到中间

import { useState, useRef } from react import { FlatList, View, Text, StyleSheet, TouchableOpacity } from react-nativeconst Center () > {const tabs ["语文", "数学", "英语", "政治", "历史", "地理&q…...

单例模式中的线程安全问题

小王学习录 本日鸡汤:单例模式什么是单例模式如何实现单例模式饿汉模式懒汉模式单例模式下的线程安全问题为什么在单例模式下会出现线程安全问题如何解决单例模式中的线程安全问题本日鸡汤: 志在山顶的人, 不会贪恋山腰的风景 单例模式 单例模式是设计模式中的一种, 所谓设计…...

Android 13 骁龙相机点击拍照流程分析(一)——点击拍照到更新到左下角缩略图

一.背景 由于最近客户定制需要将文件挂载类型修改为sdcardfs,由于修改了文件挂载类型,导致了骁龙相机拍照后不能点击进入相册,故对骁龙相机从点击事件开始进行问题的排查,此处不介绍最终的sdcardfs挂载后的问题解决方案 二.流程介绍 拍照的流程大概分为几个阶段:打开相机…...

Docker 的网络与数据管理

Docker 网络实现原理 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机…...

在QGIS中给矢量数据属性编号的一种方法

目录 写在文章前 一、给要素编号用哪些功能 二、实现一个最简单的编号 1.数据准备 2.编辑字段计算器表达式 3.查看编号结果 三、更加复杂的编号 1.使用UUID编号 2.根据单个属性排序后编号 3.根据多个属性排序后编号 4.拼接字符串进行编号 5.根据时间编号及实现 写在…...

对一个变速器原理的分析

背景 原本是朋友在调试一个看起来比较新的变速器驱动,整体来说支持两种变速模式,一种是进程级,这种用了HOOK,中规中矩的实现,原理网上都有。另一种是”系统级内核全局变速“,这个模式初步看了下有些特殊&a…...

秒验:可以自定义UI的一键登录服务

一键登录如今成为越来越多移动应用的首选,但千篇一律的登陆界面在引发用户担忧其安全性的同时,也容易让用户在不同APP切换时产生误解。因此,由国内知名移动应用开发服务商MobTech打造的一键登录工具——秒验,通过允许开发者自定义…...

pmm最新版本v2.40.0尝鲜体验

1 概述 PMM 是一款免费开源的企业级的数据库监控工具,可用来监控 MySQL、MongoDB 和 PostgreSQL 等数据库。除了指标监控,针对MySQL还具备SQL语句的性能监控。 官方地址是https://docs.percona.com/percona-monitoring-and-management,最新版…...

2023年中国数据存储市场现状及发展前景预测分析

中商情报网讯:当前,新一代信息技术快速发展推动信息产业发生了重大变革,数据存储行业将很快成为信息领域一个重要的产业分支。生成式人工智能催生算力需求,各种新兴应用场景对数据存储的容量、效率、流动性和安全性等方面提出了更…...

xlsx冻结单元格

说明 因为最近需要实现前端导出 excel 文件,并且对导出文件的样式进行一些修改,比如颜色、字体、合并单元格等,所以我找到了xlsx-style这个项目,它可以对导出的 excel 文件进行一些样式上的修改,这个项目是SheetJs的一…...

yolov8剪枝实践

本文使用的剪枝库是torch-pruning ,实验了该库的三个剪枝算法GroupNormPruner、BNScalePruner和GrowingRegPruner。 安装使用 安装依赖库 pip install torch-pruning 把 https://github.com/VainF/Torch-Pruning/blob/master/examples/yolov8/yolov8_pruning.py&…...

功能基础篇6——系统接口,操作系统与解释器系统

系统 os Python标准库,os模块提供Python与多种操作系统交互的接口 import os import stat# 文件夹 print(os.mkdir(r./dir)) # None 新建单级空文件夹 print(os.rmdir(r./dir)) # None 删除单级空文件夹 print(os.makedirs(r.\dir\dir\dir)) # None 递归创建空…...

由于导线材质不同绕组直流电阻不平衡率超标

实测证明, 有的变压器绕组的直流电阻偏大, 有的偏差较大, 其主要原因是某些导线的铜和银的含量低于国家标准规定限额。 有时即使采用合格的导线, 但由于导线截面尺寸偏差不同, 也可以导致绕组直流电阻不平衡率超标。  …...

选择智慧公厕解决方案,开创智慧城市公共厕所新时代

在城市建设和发展中,公厕作为一个不可或缺的城市基础设施,直接关系到城市形象的提升和居民生活品质的改善。然而,传统的公厕存在着管理不便、卫生状况差、设施陈旧等问题。为了解决这些困扰着城市发展的难题,智慧公厕源头厂家广州…...

FFmpeg 基础模块:AVIO、AVDictionary 与 AVOption

目录 AVIO AVDictionary 与 AVOption 小结 思考 我们了解了 AVFormat 中的 API 接口的功能,从实际操作经验看,这些接口是可以满足大多数音视频的 mux 与 demux,或者说 remux 场景的。但是除此之外,在日常使用 API 开发应用的时…...

代数——第3章——向量空间

第三章 向量空间(Vector Spaces) fmmer mit den einfachsten Beispielen anfangen. (始终从最简单的例子开始。) ------------------------------David Hilbert 3.1 (R^n)的子空间 我们的向量空间的基础模型(本章主题)是n 维实向量空间 的子空间。我们将在本节讨论它。…...

C++中显示与隐式加载dll的使用与区别

一、什么是 DLL?DLL(Dynamic Link Library) 是 Windows 下的动态链接库,包含可被多个程序共享的函数、资源或类。使用 DLL 可以实现代码复用、模块化设计和插件机制。在 C 中,调用 DLL 中的函数有两种主要方式&#xf…...

网易云音乐NCM转MP3终极指南:ncmdump工具完整使用教程

网易云音乐NCM转MP3终极指南:ncmdump工具完整使用教程 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经从网易云音乐下载了心爱的歌曲,却发现只能在特定播放器上收听?NCM格式的限制让音乐…...

苏州创新药20年,站上全球产业洗牌暴风眼

一个城市的创新药产业集群如何从无到有,又如何在全球化临界点寻找自己的位置。文|徐鑫编|任晓渔过去一年多,苏州是全球创新药产业版图中一个绕不过去的城市。大额海外授权交易频繁传出,在中国高端制造走出去的背景下&a…...

智慧树自动刷课助手:3步告别手动操作的学习效率工具

智慧树自动刷课助手:3步告别手动操作的学习效率工具 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 还在为智慧树平台的重复刷课操作而烦恼吗?智…...

Python PIL 画矩形框

基础代码 from PIL import Image, ImageDraw# 打开图片 img Image.open(your_image.jpg)# 创建绘图对象 draw ImageDraw.Draw(img)# 矩形坐标 (x1, y1, x2, y2) coords (23, 21, 69, 76)# 画矩形框(红色,线宽2) draw.rectangle(coords, ou…...

3大实战秘籍:揭秘raylib如何让游戏开发像搭积木一样简单

3大实战秘籍:揭秘raylib如何让游戏开发像搭积木一样简单 【免费下载链接】raylib A simple and easy-to-use library to enjoy videogames programming 项目地址: https://gitcode.com/GitHub_Trending/ra/raylib 你是否曾经被复杂的游戏引擎配置搞得焦头烂额…...

Postgresql基础实践教程(九)

⭐️⭐️⭐️⭐️⭐️ 完整数据详见 练习数据免费 ⭐️⭐️⭐️⭐️⭐️ 七十二、WITH查询(公用表表达式CTE) 1. SELECT 中的 WITH 2. 递归查询 3. 公用表表达式的物化 4. WITH中的数据修改语句 WITH提供了一种在主查询中写辅助语句的方法。这些语…...

基于随机森林的低成本传感器机器学习校准实践指南

1. 项目概述:当低成本传感器遇上机器学习校准在物联网和智能感知系统铺天盖地的今天,低成本传感器几乎无处不在。从监测办公室的空气质量,到追踪城市街道的噪音污染,再到农业大棚里的温湿度控制,这些价格亲民的“小眼睛…...

UE4SS终极指南:从零开始掌握虚幻引擎脚本系统

UE4SS终极指南:从零开始掌握虚幻引擎脚本系统 【免费下载链接】RE-UE4SS Injectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games 项目地址: https://gitcode.com/gh_mirrors/re/RE-UE4SS UE4S…...

D3KeyHelper终极指南:5分钟掌握暗黑3最强自动化工具

D3KeyHelper终极指南:5分钟掌握暗黑3最强自动化工具 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面,可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper D3KeyHelper是一款专为《暗黑破坏神3…...