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

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

跟着流程配置:

mybatis-config

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

Mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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:

@Testpublic void testGlobalCache() throws IOException {// 加载配置文件InputStream is = Resources.getResourceAsStream("mybatis-config.xml");// 创建 SqlSessionFactorySqlSessionFactory 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");// 创建 SqlSessionFactorySqlSessionFactory 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.EmployeesEntityat 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.EmployeesEntityat 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;}@Overridepublic 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 mapperPUBLIC "-//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.mzph.cn/web/38492.shtml

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

相关文章

你喜欢波段交易吗?

波段交易的核心在于精准捕捉市场中的长期趋势波动&#xff0c;以实现更为稳健的收益。与剥头皮和日内交易不同&#xff0c;波段交易者更倾向于持有交易头寸数日乃至数周&#xff0c;以更宽广的视角把握市场动态。 这种交易方式的优势在于&#xff0c;它降低了对即时市场反应的…

【Gin】项目搭建 一

环境准备 首先确保自己电脑安装了Golang 开始项目 1、初始化项目 mkdir gin-hello; # 创建文件夹 cd gin-hello; # 需要到刚创建的文件夹里操作 go mod init goserver; # 初始化项目&#xff0c;项目名称&#xff1a;goserver go get -u github.com/gin-gonic/gin; # 下载…

动态规划算法,完全零基础小白教程!不是计算机的都能学会!万字吐血详解。

目录 一、动态规划算法概念 题一 1、算法解析 1&#xff09;确定状态&#xff1a; ​2&#xff09;状态转移方程&#xff1a; ​3&#xff09;初始化&#xff1a; 4&#xff09;填表顺序&#xff1a; 5&#xff09;返回值&#xff1a; 2、代码 题二 1、算法解析 1、确…

如何理解MySql的MVCC机制

MVCC是什么 MySQL的MVCC机制&#xff0c;全称为多版本并发控制&#xff08;Multi-VersionConcurrency Control&#xff09;&#xff0c;是一种提高数据库并发性能的技术。MVCC的主要目的是在保证数据一致性的同时&#xff0c;提高数据库的并发性能。 它通过为每个读操作创建数…

【高中数学/三角函数】已知:x,y皆为实数,且4x^2+y^2+xy=1 求:2x+y的最大值

【问题】 已知&#xff1a;x,y皆为实数&#xff0c;且4x^2y^2xy1 求&#xff1a;2xy的最大值 【问题来源】 https://www.ixigua.com/7289764285772497448?logTag0d228277f3a8e049ab6d 【解答】 解&#xff1a; 由4x^2y^2xy1 可得 15/4*x^21/4*x^2xyy^21 得到(15开方/…

智能版面设计:指令跟随模型在自动布局规划中的应用

在广告行业一个吸引人的视觉布局能够显著提升信息的传播效果。但对于非专业设计师来说&#xff0c;创建既美观又功能性强的布局常常是一项挑战。他们往往缺乏必要的设计技能、审美训练或资源来快速实现创意构想。传统的设计软件和在线工具虽然提供了一些模板和指导&#xff0c;…

0702_ARM6

练习&#xff1a; 中断实验 main.c #include "key.h" int main() {//初始化rcc gpiohal_key_rcc_gpio_init();//初始化extihal_key_exti_init();//初始化gichal_key_gic_init();while(1){}return 0; }key.c #include "key.h"//GPIOF初始化 void hal_key_…

Linux的一些杂项函数总结

getopt_long 解析命令行。 参考&#xff1a; C语言linux getopt_long()函数&#xff08;命令行解析&#xff09;&#xff08;getopt、getopt_long_only&#xff09;&#xff08;短选项 -&#xff0c;长选项 --&#xff09;&#xff08;option结构体&#xff09;&#xff08;opt…

vue3-openlayers marker 光晕扩散(光环扩散)(postrender 事件和 render 方法)

本篇介绍一下使用 vue3-openlayers marker 光晕扩散&#xff08;光环扩散&#xff09;&#xff08;postrender 事件和 render 方法&#xff09; 1 需求 marker 光晕扩散&#xff08;光环扩散&#xff09; 2 分析 marker 光晕扩散&#xff08;光环扩散&#xff09;使用 post…

中级java每日一道面试题-2024年7月2日

题目&#xff1a; 请解释一下 Java 中的线程安全问题&#xff0c;并提供一些常见的解决方法。 答案&#xff1a; 线程安全问题是指在多线程环境下&#xff0c;多个线程同时访问共享资源时可能出现的数据不一致或错误的情况。这可能导致程序的不可预测性和错误的结果。 常见的…

徐州三线服务器租用的优势有哪些?

对于单线服务器与双线服务器来说&#xff0c;三线服务器是能够同时拥有电信、联通和移动三条线路的服务器&#xff0c;同时也被称为三线路由器或者是三线宽带路由器&#xff0c;有着三个独立的网卡和三个IP地址&#xff0c;使用户无论是通过哪些线路连接都能够进入服务器&#…

android.bp 静态库 依赖 动态库

在Android平台上&#xff0c;使用Android.bp文件来定义和构建Android静态库&#xff08;.so文件&#xff09;和动态库&#xff08;.so文件&#xff09;之间的依赖关系是很常见的。以下是一个简单的例子&#xff0c;展示了如何在Android.bp文件中定义一个静态库&#xff0c;它依…

SPI NAND、SD NAND和eMMC对比—MK米客方德

目录 1. 容量: 2.封装类型&#xff1a; 3.速度: 4.性能: 5.寿命: 6. 使用方式: 7. 其他优缺点: 8.常见应用场景: 1. 容量: SPI NAND通常提供从几百MB到几GB的存储容量。 SD NAND的容量覆盖范围比SPI NAND更广&#xff0c;从几GB到几十GB不等。 eMMC的容量范围更大&a…

代码随想录第41天|动态规划

322. 零钱兑换 dp[j] : 最小硬币数量, j 为金额(相当于背包空间)递推公式 : dp[j] min(dp[j - coins[i]] 1, dp[j])初始化: 需要一个最大值, 避免覆盖, dp[0] 0遍历顺序: 钱币有序无序不影响, 因为求解最小个数, 结果相同(先遍历物品后背包, 先背包后物品都可) class Solut…

【chatgpt】两层gcn提取最后一层节点输出特征,如何自定义简单数据集

文章目录 两层gcn&#xff0c;提取最后一层节点输出特征&#xff0c;10个节点&#xff0c;每个节点8个特征&#xff0c;连接关系随机生成&#xff08;无全连接层&#xff09;如何计算MSE 100个样本&#xff0c;并且使用批量大小为32进行训练第一个版本定义数据集出错&#xff0…

怎样在《语文世界》期刊上发表论文?

怎样在《语文世界》期刊上发表论文&#xff1f; 《语文世界》知网国家级 1.5-2版 2500字符左右 正常收25年4-6月版面 可加急24年内&#xff08;初中&#xff0c;高中&#xff0c;中职&#xff0c;高职&#xff0c;大学均可&#xff0c;操作周期2个月左右&#xff09; 《语文世…

【084】基于SpringBoot实现的家乡特色推荐系统

系统介绍 视频演示 点击查看演示视频 基于SpringBoot实现的家乡特色推荐系统主要采用SpringBootVue进行开发&#xff0c;系统整体分为管理员、用户两种角色&#xff0c;主要功能包括首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;文章分类管理&#xff0c;文章分…

C语言结构体深入解析【结构体嵌套结构体,结构体变量和指针,结构体和函数,计算结构体大小,结构体数组,结构体成员的访问,结构体与联合】

C语言结构体深入解析 目录 C语言结构体深入解析前言结构体的定义结构体在内存中的表示结构体变量初始化直接定义并初始化使用自己定义的结构体变量初始化新变量结构体数组初始化 结构体中嵌套结构体结构体成员访问点操作符(.)箭头操作符(->) 结构体变量和指针结构体指针定义…

TensorFlow代码逻辑 vs PyTorch代码逻辑

文章目录 一、TensorFlow&#xff08;一&#xff09;导入必要的库&#xff08;二&#xff09;加载MNIST数据集&#xff08;三&#xff09;数据预处理&#xff08;四&#xff09;构建神经网络模型&#xff08;五&#xff09;编译模型&#xff08;六&#xff09;训练模型&#xf…

@RequestMapping属性详解及案例演示

RequestMapping源码 Target({ElementType.TYPE, ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Documented Mapping public interface RequestMapping {String name() default "";AliasFor("path")String[] value() default {};AliasFor(&quo…