关于 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;提高数据库的并发性能。 它通过为每个读操作创建数…

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

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

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

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

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…

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

怎样在《语文世界》期刊上发表论文&#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语言结构体深入解析前言结构体的定义结构体在内存中的表示结构体变量初始化直接定义并初始化使用自己定义的结构体变量初始化新变量结构体数组初始化 结构体中嵌套结构体结构体成员访问点操作符(.)箭头操作符(->) 结构体变量和指针结构体指针定义…

@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…

智能写作与痕迹消除:AI在创意文案和论文去痕中的应用

作为一名AI爱好者&#xff0c;我积累了许多实用的AI生成工具。今天&#xff0c;我想分享一些我经常使用的工具&#xff0c;这些工具不仅能帮助提升工作效率&#xff0c;还能激发创意思维。 我们都知道&#xff0c;随着技术的进步&#xff0c;AI生成工具已经变得越来越智能&…

简单分享 for循环,从基础到高级

1. 基础篇&#xff1a;Hello, For Loop! 想象一下&#xff0c;你想给班上的每位同学发送“Hello!”&#xff0c;怎么办&#xff1f;那就是for循环啦&#xff0c; eg&#xff1a;首先有个名字的列表&#xff0c;for循环取出&#xff0c;分别打印 names ["Alice", …

bigNumber的部分使用方法与属性

场景&#xff1a;最近做IoT项目的时候碰到一个问题&#xff0c;涉及到双精度浮点型的数据范围的校验问题。业务上其实有三种类型&#xff1a;int、float和double类型三种。他们的范围分别是&#xff1a; //int int: [-2147483648, 2147483647],//float float: [-3402823466385…

PHP7源码结构

PHP7程序的执行过程 1.PHP代码经过词法分析转换为有意义的Token&#xff1b; 2.Token经过语法分析生成AST&#xff08;Abstract Synstract Syntax Tree&#xff0c;抽象语法树&#xff09;&#xff1b; 3.AST生成对应的opcode&#xff0c;被虚拟机执行。 源码结构&#xff1…

一切为了安全丨2024中国应急(消防)品牌巡展武汉站成功召开!

消防品牌巡展武汉站 6月28日&#xff0c;由中国安全产业协会指导&#xff0c;中国安全产业协会应急创新分会、应急救援产业网联合主办&#xff0c;湖北消防协会协办的“一切为了安全”2024年中国应急(消防)品牌巡展-武汉站成功举办。该巡展旨在展示中国应急&#xff08;消防&am…

qt QTreeView的简单使用(多级子节点)

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);setWindowTitle("QTreeView的简单使用");model new QStandardItemModel;model->setHorizontalHeaderLabels(QStringList() << "left&q…

[leetcode]longest-arithmetic-subsequence-of-given-difference. 最长定差子序列

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int longestSubsequence(vector<int> &arr, int difference) {int ans 0;unordered_map<int, int> dp;for (int v: arr) {dp[v] dp[v - difference] 1;ans max(ans, dp[v]);}return ans…

Qt源码分析:窗体绘制与响应

作为一套开源跨平台的UI代码库&#xff0c;窗体绘制与响应自然是最为基本的功能。在前面的博文中&#xff0c;已就Qt中的元对象系统(反射机制)、事件循环等基础内容进行了分析&#xff0c;并捎带阐述了窗体响应相关的内容。因此&#xff0c;本文着重分析Qt中窗体绘制相关的内容…