关于 Mybatis 的开启二级缓存返回对象不一致问题

news2025/1/1 22:23:51

        做实验报告的时候,跟着学习,发现我已经将 开启 二级缓存的 配置都配置好了,但是返回值地址不一致,说明对象不一致,二级缓存命中失败。

跟着流程配置:

mybatis-config

<settings>
	 <!-- 启用 mybatis 全局缓存 -->
    <setting name="cacheEnabled" value="true"/>
    
    <setting name="logImpl" value="LOG4J"/>
</settings>

Mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.Angindem.mapper.DynamicMapper">
	
	<cache/>
	
	<select id="queryByEntities" resultType="com.Angindem.Entity.EmployeesEntity" useCache="true">
		select * from employees
		 <where>
			<foreach collection="emp" index="fld" item="val" separator="and">
				${fld} = #{val}
			</foreach>
		 </where>
	</select>
	
</mapper>

TestCode:

@Test
	public void testGlobalCache() throws IOException 
	{
		// 加载配置文件
		InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
		// 创建 SqlSessionFactory
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		
		
		// 创建 sqlsession(得到 sql 语句,并执行)
		SqlSession session1 = factory.openSession(true); // 如果参数为空 默认 false 手动提交事务
		// 获取 mapper 对象(代理模式,可以囊组返回当前接口的实现类对象)
		DynamicMapper dynamicMapper1 = session1.getMapper(DynamicMapper.class);
		SqlSession session2 = factory.openSession(true); // 如果参数为空 默认 false 手动提交事务
		DynamicMapper dynamicMapper2 = session2.getMapper(DynamicMapper.class);
		
		Map<String, Object> emp = new HashMap<>();
		emp.put("OfficeCode", 1);
		
		// 调用 SqlSession 执行一次 Mybatis,保存到本地缓存(二级级缓存)
		List<EmployeesEntity> list = dynamicMapper1.queryByEntities(emp);
		
		
		// 重复 操作 list2 接受 该语句
		List<EmployeesEntity> list2 = dynamicMapper2.queryByEntities(emp);
		
		
		// 判断是否命中本地缓存,如果命中了,则返回的地址是相同的
		System.out.println("所接受的两个链表地址一致?     结果:" + (list == list2));
		
		System.out.println("===================================\n\n");
		list.stream().forEach(s -> System.out.println(s));
	}

最后运行效果:

上网查资料,说 需要将运行的SqlSession会话关闭:

        根据我的理解,需要将 SqlSession 关闭,本地缓存 与 SqlSession 的绑定,解绑后,将本地缓存,传到 二级缓存那里才可以。

修改后的 TestCode:

public void testGlobalCache() throws IOException 
	{
		// 加载配置文件
		InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
		// 创建 SqlSessionFactory
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		
		
		// 创建 sqlsession(得到 sql 语句,并执行)
		SqlSession session1 = factory.openSession(true); // 如果参数为空 默认 false 手动提交事务
		// 获取 mapper 对象(代理模式,可以囊组返回当前接口的实现类对象)
		DynamicMapper dynamicMapper1 = session1.getMapper(DynamicMapper.class);
		SqlSession session2 = factory.openSession(true); // 如果参数为空 默认 false 手动提交事务
		DynamicMapper dynamicMapper2 = session2.getMapper(DynamicMapper.class);
		
		Map<String, Object> emp = new HashMap<>();
		emp.put("OfficeCode", 1);
		
		// 调用 SqlSession 执行一次 Mybatis,保存到本地缓存(一级缓存)
		List<EmployeesEntity> list = dynamicMapper1.queryByEntities(emp);
		
		// 关闭当前 SqlSession ,解绑 一级缓存,将语句 放到二级缓存
		session1.close();
		
		// 重复 操作 list2 接受 该语句
		List<EmployeesEntity> list2 = dynamicMapper2.queryByEntities(emp);
		
		// 关闭当前 SqlSession ,解绑 一级缓存,将语句 放到二级缓存
		session2.close();
		
		// 判断是否命中本地缓存,如果命中了,则返回的地址是相同的
		System.out.println("所接受的两个链表地址一致?     结果:" + (list == list2));
		
		System.out.println("===================================\n\n");
		list.stream().forEach(s -> System.out.println(s));
	}

最后运行效果:

报错了,信息如下:

org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: com.Angindem.Entity.EmployeesEntity
	at org.apache.ibatis.cache.decorators.SerializedCache.serialize(SerializedCache.java:95)
	at org.apache.ibatis.cache.decorators.SerializedCache.putObject(SerializedCache.java:56)
	at org.apache.ibatis.cache.decorators.LoggingCache.putObject(LoggingCache.java:49)
	at org.apache.ibatis.cache.decorators.SynchronizedCache.putObject(SynchronizedCache.java:43)
	at org.apache.ibatis.cache.decorators.TransactionalCache.flushPendingEntries(TransactionalCache.java:116)
	at org.apache.ibatis.cache.decorators.TransactionalCache.commit(TransactionalCache.java:99)
	at org.apache.ibatis.cache.TransactionalCacheManager.commit(TransactionalCacheManager.java:45)
	at org.apache.ibatis.executor.CachingExecutor.close(CachingExecutor.java:61)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.close(DefaultSqlSession.java:260)
	at com.Angindem.test.TestOffice.testGlobalCache(TestOffice.java:241)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:757)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: java.io.NotSerializableException: com.Angindem.Entity.EmployeesEntity
	at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1197)
	at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
	at java.base/java.util.ArrayList.writeObject(ArrayList.java:866)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1074)
	at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1526)
	at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1448)
	at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1191)
	at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
	at org.apache.ibatis.cache.decorators.SerializedCache.serialize(SerializedCache.java:91)
	... 35 more

抛出了关键的异常:   java.io.NotSerializableException

继续查资料,解释了一下这个异常:

java.io.NotSerializableException
java.io.NotSerializableException 是一个在 Java 程序中抛出的异常,属于 java.io 包。这个异常表明尝试序列化一个对象时,该对象的类没有实现 java.io.Serializable 接口。序列化是 Java 中的一个机制,允许将对象的状态保存到文件或通过网络发送,以便之后可以重新创建该对象。

根据我自己的理解,Mybatis 中的二级缓存,就是需要将我们执行后的语句的本地缓存,保存并且上传到 mybatis 的二级缓存机制中,所以需要将 对应的 Entity 对象 进行序列化,所以要在相应的类,加个接口,Serializable

修改我对应的类代码:

package com.Angindem.Entity;

import java.io.Serializable;

public class EmployeesEntity implements Serializable {
	private Integer employeeNumber;
	private String lastName;
	private String firstName;
	private String extension;
	private String email;
	private Integer officeCode;
	private String jobTitle;
	
	public EmployeesEntity(Integer employeeNumber, String lastName, String firstName, String jobTitle) {
		super();
		this.employeeNumber = employeeNumber;
		this.lastName = lastName;
		this.firstName = firstName;
		this.jobTitle = jobTitle;
	}
	
	public Integer getEmployeeNumber() {
		return this.employeeNumber;
	}
	public void setEmployeeNumber(Integer employeeNumber) {
		this.employeeNumber = employeeNumber;
	}
	public String getlastName() {
		return lastName;
	}
	public void setlastName(String lastName) {
		this.lastName = lastName;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getExtension() {
		return extension;
	}
	public void setExtension(String extension) {
		this.extension = extension;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public Integer getOfficeCode() {
		return officeCode;
	}
	public void setOfficeCode(Integer officeCode) {
		this.officeCode = officeCode;
	}
	public String getJobTitle() {
		return jobTitle;
	}
	public void setJobTitle(String jobTitle) {
		this.jobTitle = jobTitle;
	}
	@Override
	public String toString() {
		return "Employees [employeeNumber=" + employeeNumber + ", lastName=" + lastName + ", firstName=" + firstName
				+ ", extension=" + extension + ", email=" + email + ", officeCode=" + officeCode + ", jobTitle="
				+ jobTitle + "]";
	}
}

最后运行效果:

运行成功了,发现还是没有命中二级缓存

继续查资料,原来标签<cache/>有问题

修改后的 Mapper:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.Angindem.mapper.DynamicMapper">
	
	<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
	
	<select id="queryByEntities" resultType="com.Angindem.Entity.EmployeesEntity" useCache="true">
		select * from employees
		 <where>
			<foreach collection="emp" index="fld" item="val" separator="and">
				${fld} = #{val}
			</foreach>
		 </where>
	</select>
</mapper>

这里解释一下:

详细配置的 <cache> 标签:

        MyBatis 的 <cache> 标签用于配置 Mapper 级别的缓存行为。以下是两个 <cache> 标签配置的区别:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
  • eviction="FIFO": 指定了缓存的逐出策略为先进先出(FIFO)。这是当缓存达到其最大容量时用来决定哪些对象应该被移除的算法。

  • flushInterval="60000": 指定了缓存刷新的时间间隔,单位为毫秒。这里设置为 60000 毫秒,即每 60 秒缓存会被清空一次。

  • size="512": 指定了缓存中可以存储的对象数量上限。这里设置为最多 512 个对象。

  • readOnly="true": 指定了缓存中的对象是只读的。这通常可以提高性能,因为 MyBatis 不需要在每次查询后都同步数据。

默认配置的 <cache> 标签:

<cache/>

<cache> 标签没有包含任何属性时,MyBatis 将使用默认的缓存配置。默认配置通常包括:

  • 使用 LRU(最近最少使用)逐出策略。

  • 没有设置缓存刷新的时间间隔,缓存会在每次会话结束时清空。

  • 默认的缓存大小没有明确限制,但实际大小可能会受到 JVM 内存限制。

  • 缓存中的对象不是只读的,这意味着它们可以被修改。

总结来说,第一个 <cache> 标签提供了详细的缓存行为配置,包括逐出策略、刷新间隔、缓存大小和只读属性。而第二个 <cache> 标签则使用 MyBatis 的默认缓存配置,没有显式设置这些属性。使用详细的配置可以帮助开发者根据应用的具体需求来优化缓存性能。

我的理解是,如果使用默认的<cache> 标签,我们关闭了SqlSession会话后,其中的二级缓存也会在 每次的 会话结束时清空,所以我们没有命中到前一个缓存。

最后成功运行效果:

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

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

相关文章

mst[讲课留档]

最小生成树(Minimum Spanning Tree) (1)概念 我们知道&#xff0c;树是有 n n n个结点&#xff0c; n − 1 n-1 n−1条边的无向无环的连通图。 一个连通图的生成树是一个极小的连通子图&#xff0c;它包含图中全部的 n n n个顶点&#xff0c;但只有构成一棵树的 n − 1 n-1 …

实验五 计数器的设计与仿真

仿真 链接&#xff1a;https://pan.baidu.com/s/1N1nR39Gws59laVZY2slzBw 提取码&#xff1a;01ct 一、实验目的 1、通过实验&#xff0c;能熟悉QUARTUS开发环境&#xff0c;能够掌握VHDL设计电路&#xff0c;掌握使用相关仿真工具进行功能和时序仿真的方法&#xff1b; 2、通…

.js.map文件泄露/Springboot信息泄露

目录 框架识别 Webpack 简述 .js.map文件泄露 利用 Spring boot 很多网站都使用的是现有的框架进行开发的&#xff0c;因此相当于很多目录和文件的路径都是开源可知的&#xff0c;因此我们就可以直接访问对应的路径&#xff0c;如果网站没有进行限制就有可能会导致敏感信…

Mac搭建anaconda环境并安装深度学习库

1. 下载anaconda安装包 根据自己的操作系统不同&#xff0c;选择不同的安装包Anaconda3-2024.06-1-MacOSX-x86_64.pkg&#xff0c;我用的还是旧的intel所以下载这个&#xff0c;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/&#xff0c;如果mac用的是M1&#xff0…

Administrators就最高了???system是什么??本地用户提权内网学习第三天 你知道uac是什么??

我们今天来说说本地用户提权的操作&#xff0c;我们在有webshell过后我们要进行进一步的提权操作&#xff0c;要不然对我们后期的内网渗透会有一些阻碍的操作。比如说我们使用mimikatz来进行抓取密码&#xff0c;就不能够成功。 Administrators与system的区别 我们来说说Admin…

毫米波雷达深度学习技术-1.7训练一个神经网络

1.7 训练一个神经网络 对于训练神经网络&#xff0c;有两个步骤&#xff0c;即前向传递和误差反向传播。 1.7.1 前向传播和反向传播 在前向传递中&#xff0c;输入被馈送到模型并与权重向量相乘&#xff0c;并为每一层添加偏差以计算模型的输出。密集层或全连接层第l层的输入、…

微信小程序的运行机制与更新机制

1. 小程序运行机制 1.1. 冷启动与热启动 冷启动为用户第一次打开小程序时&#xff0c;因为之前没有打开过&#xff0c;这是第一种冷启动的情兑。第二种情况为虽然之前用户打开过&#xff0c;但是小程序被用户主动的销毁过&#xff0c;这种情况下我们再次打开小程序&#xff0…

西门子S120伺服驱动器F1910故障报警处理总结

西门子S120伺服驱动器F1910故障报警处理总结 热压机正常工作时出现故障,无上升和下降动作,伺服故障代码为1910, 同时发现压机的实际压力为13Mpa,没有达到设定的14Mpa, 查看S120的报警手册,如下图所示, F01910:现场总线设定值超时,与上位机控制器的通讯故障, 可能的原…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑复合指标优化模态分解和 Stacking 集成的综合能源系统多元负荷预测》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

巴西东南湾乌巴图巴 ANTARES 监测站数据

ANTARES monitoring station in Ubatuba, Southeast Brazilian Bight 巴西东南湾乌巴图巴 ANTARES 监测站 简介 ANTARES 区域网络由分布在拉丁美洲的沿岸时间序列站组成。主要目的是研究气候和人为影响引起的长期变化&#xff0c;以及用于卫星匹配和算法开发的海洋颜色。Uba…

一分钟学习数据安全—自主管理身份SSI分布式加密密钥管理

在这篇之前&#xff0c;我们已经对SSI有了一个全局的了解。这个系列的文章可以作为一个学习笔记来参考&#xff0c;真正要实践其中的一些方案、协议&#xff0c;还需要参考专业的书籍和官方文档。作为一个SSI系列学习笔记的最后一篇&#xff0c;我们做一个简单的延伸&#xff0…

【PLC】三菱PLC如何和汇川伺服实现485通信

前言 一开始选用的是汇川SV660P脉冲型伺服&#xff0c;由于生产需求需要对伺服的个别参数进行读取和写入操作&#xff0c;但是SV660P并不支持这种情况&#xff0c;因此需要使用485通信来满足。PLC这边选用的是三菱FX5U。 开始 1、首先准备按照下图的引脚提示准备好一根带屏蔽…

(七)glDrawArry绘制

几何数据&#xff1a;vao和vbo 材质程序&#xff1a;vs和fs(顶点着色器和片元着色器) 接下来只需要告诉GPU&#xff0c;使用几何数据和材质程序来进行绘制。 #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <iostrea…

英伟达经济学:云服务商在GPU上每花1美元 就能赚7美元

NVIDIA超大规模和 HPC 业务副总裁兼总经理 Ian Buck 近日在美国银行证券 2024 年全球技术大会上表示&#xff0c;客户正在投资数十亿美元购买新的NVIDIA硬件&#xff0c;以跟上更新的 AI 大模型的需求&#xff0c;从而提高收入和生产力。 Buck表示&#xff0c;竞相建设大型数据…

flask中解决图片不显示的问题(很细微的点)

我在编写flask项目的时候&#xff0c;在编写html的时候&#xff0c;发现不管我的图片路径如何变化&#xff0c;其就是显示不出来。如下图我框中的地方。 我尝试过使用浏览器打开&#xff0c;是可以的。 一旦运行这个flask项目&#xff0c;就无法显示了。 我查阅资料后。发现…

Kafka-时间轮和延迟操作-源码流程

TimingWheel 字段&#xff1a; buckets&#xff1a;Array.tabulate[TimerTaskList]类型&#xff0c;其每一个项都对应时间轮中的一个时间格&#xff0c;用于保存 TimerTaskList的数组。在TimingWheel中&#xff0c;同一个TimerTaskList中的不同定时任务的到期时间可能 不同&a…

【Dison夏令营 Day 06】用 Python 和 Rich 制作 Wordle克隆(中篇)

在大流行期间&#xff0c;Wordle 在 Twitter 上还算比较流行的一款基于网络的益智游戏&#xff0c;要求玩家每天在六次或更短时间内猜出一个新的五个字母的单词&#xff0c;每个人得到的单词都是一样的。 在本教程中&#xff0c;你将在终端上创建自己的 Wordle 克隆。自 2021 …

【Qt】认识Qt界面Hello world小程序

一.认识Qt界面 1.左边栏 在编辑模式下&#xff0c;左边竖排的两个窗⼝叫做 "边栏" 。 ① 是项⽬⽂件管理窗⼝ ② 是打开⽂件列表窗⼝。 边栏⾥的窗⼝数⽬可以增加&#xff0c;边栏⼦窗⼝标题栏有⼀排⼩按钮&#xff0c;最右边的是关闭按钮&#xff0c;倒数第⼆个是 …

分布式限流:Spring Cloud Gateway 限流

分布式限流&#xff1a;Spring Cloud Gateway 限流 在现代微服务架构中&#xff0c;流量控制是一个至关重要的部分。分布式限流作为一种有效的流量控制手段&#xff0c;能够帮助我们保护系统不被突发的流量冲垮。Spring Cloud Gateway支持多种限流方式。 什么是分布式限流 分…

电影交流平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;电影类型管理&#xff0c;留言反馈管理&#xff0c;电影中心管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;电影中心&#xff0c;留言反馈 开发系统&#xff1a;Window…