关于 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 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.mfbz.cn/a/766198.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot+Thymeleaf项目重定向到另一个系统HTTPS变成HTTP

SpringBootThymeleaf项目是一个简单的单体项目&#xff0c;只有一个页面。 重定向的是前后分离&#xff0c;前端用的vue。 浏览器看到重定向后 https成了http&#xff0c;F12控制台看到是 301 Moved Permanently 单体项目最开始写法&#xff1a; response.sendRedirect(url); …

react native中使用@react-navigation/native进行自定义头部

react native中使用react-navigation/native进行自定义头部 效果示例图实例代码 效果示例图 实例代码 /* eslint-disable react-native/no-inline-styles */ /* eslint-disable react/no-unstable-nested-components */ import React, { useLayoutEffect } from react; import…

基于uniapp(vue3)H5附件上传组件,可限制文件大小

代码&#xff1a; <template><view class"upload-file"><text>最多上传5份附件&#xff0c;需小于50M</text><view class"" click"selectFile">上传</view></view><view class"list" v…

html+css+js气球消除小游戏

气球消除小游戏 消除15个就成功 源码在图片后 点赞加关注&#xff0c;谢谢 左上角的数字显示消除气球的数量 定时随机生成气球 &#x1f388;&#x1f388;&#x1f388; 图片 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset&…

C++部分复习笔记上

C语法复习 1. C入门基础 缺省参数 半缺省参数必须从右往左依次来给出&#xff0c;不能间隔着给缺省参数不能在函数声明和定义中同时出现缺省值必须是常量或者全局变量C语言不支持&#xff08;编译器不支持&#xff09; 函数重载 函数重载是函数的一种特殊情况&#xff0c;…

小试牛刀-Solana合约账户详解

目录 一.Solana 三.账户详解 3.1 程序账户 3.2 系统所有账户 3.3 程序派生账户(PDA) 3.4 Token账户 四、相关学习文档 五、在线编辑器 Welcome to Code Blocks blog 本篇文章主要介绍了 [Solana合约账户详解] ❤博主广交技术好友&#xff0c;喜欢文章的可以关注一下❤ …

企业如何管理安全生产工作?(附模板)

总结一下在企业内管理安全中遇到的一些问题&#xff1a; 1、 管理方式落后&#xff0c;还在使用纸质记录 2、 人员信息杂乱无章&#xff0c;无人整理 3、出现问题找不到源头和负责人 我做系统管理已经7年了&#xff0c;题主说的这些问题我之前也遇到过&#xff0c;相信也有…

LabVIEW幅频特性测试系统

使用LabVIEW软件开发的幅频特性测试系统。该系统整合了Agilent 83732B信号源与Agilent 8563EC频谱仪&#xff0c;通过LabVIEW编程实现自动控制和数据处理&#xff0c;提供了成本效益高、操作简便的解决方案&#xff0c;有效替代了昂贵的专用仪器&#xff0c;提高了测试效率和设…

taro小程序terser-webpack-plugin插件不生效

背景 taro小程序terser-webpack-plugin插件不生效 运行环境&#xff1a; taro3.6.32vue3.2.24 webpack5 不断尝试过 按官网这种配置不生效&#xff0c;然后又看了文档中赋的这个链接《编写插件&#xff0c;将 Taro 编译打包耗时缩短至三分之一》。 按这个文档中的配置&…

魏建军亲测智驾第二季:长城全场景NOA挑战重庆

6月30日&#xff0c;魏建军再次直播长城的全场景智能驾驶NOA。上一次直播是保定&#xff0c;而这次选在了山城重庆。 重庆号称「8D魔幻城市」&#xff0c;是每一个在智驾上有抱负的头部公司&#xff0c;都想跑一跑必争之地。 这次直播长城用的智驾系统&#xff0c;也是TA序列…

Go - 8.func 函数使用

目录 一.引言 二.func 定义 三.func 实践 1.多个返回值 2.命名返回值 3.可变参数 四.总结 一.引言 函数是编程语言中的基本构建块&#xff0c;用于将代码组织成可重用的逻辑单元。函数可以接受输入参数&#xff0c;执行特定的操作&#xff0c;并返回结果。在 Go 语言&a…

25.labview数据采集中的读取和写入文本文件和Excel表格文件

①本文将会讲解labview读取和写入文本文件和Excel文件的几种不同方式&#xff0c;讲解程序的基本原理&#xff0c;并提出具体的实施方案&#xff0c;本文内容如下所示。 ②本文文章结束会提供大家 文本和表格读取写入的源程序 &#xff0c;以便于大家学习和使用。 本文中可能用…

家政小程序的开发:打造现代式便捷家庭服务

随着现代生活节奏的加快&#xff0c;人们越来越注重生活品质与便利性。在这样的背景下&#xff0c;家政服务市场迅速崛起&#xff0c;成为许多家庭日常生活中不可或缺的一部分。然而&#xff0c;传统的家政服务往往存在信息不对称、服务效率低下等问题。为了解决这些问题&#…

Windows10录屏,教你3个方法,简单快速录屏

“我的电脑系统是Windows10的系统&#xff0c;今晚要进行线上开会&#xff0c;但我实在有事没办法参加会议&#xff0c;想把会议的内容录制下来方便我后续观看。但却找不到电脑录屏功能在哪里打开&#xff1f;求助一下&#xff0c;谁能帮帮我&#xff1f;” 在数字化时代&…

Oracle - 数据库打补丁实践

原文&#xff1a;https://www.cnblogs.com/ddzj01/p/12097467.html 一、概述 本文将介绍如何给oracle数据库打最新补丁&#xff0c;数据库版本为11.2.0.4单实例&#xff0c;操作系统为redhat6.5 二、下载相关升级包 1. 登录MOS&#xff0c;查阅(ID 2118136.2)&#xff0c;下载…

编译原理1

NFA&DFA 在正规式的等价证明可以借助正规集&#xff0c;也可以通过有限自动机DFA来证明等价&#xff0c;以下例题是针对DFA证明正规式的等价&#xff0c;主要步骤是①NFA&#xff1b;②状态转换表&#xff1b; ③状态转换矩阵&#xff1b; ④化简DFA&#xff1b; 文法和语…

20240702在飞凌OK3588-C开发板上通过HDMI OUT输出USB3.0接口的热像仪的预览图像

20240702在飞凌OK3588-C开发板上通过HDMI OUT输出USB3.0接口的热像仪的预览图像 2024/7/2 18:19 rootok3588:/# rootok3588:/# rootok3588:/# lsusb Bus 005 Device 001: ID 1d6b:0002 Bus 003 Device 001: ID 1d6b:0001 Bus 001 Device 001: ID 1d6b:0002 Bus 006 Device 00…

llama-factory训练RLHF-PPO模型

理论上RLHF&#xff08;强化学习&#xff09;效果比sft好&#xff0c;也更难训练。ppo有采用阶段,步骤比较多,训练速度很慢. 记录下工作中使用llama-factory调试rlhf-ppo算法流程及参数配置,希望对大家有所帮助. llama-factory版本: 0.8.2 一 rlhf流程 ppo训练流程图如下, 会…

【Linux】—Xshell、Xftp安装

文章目录 前言一、下载Xshell、Xftp二、安装Xshell三、使用XShell连接Linux服务器四、修改windows的主机映射文件&#xff08;hosts文件&#xff09;五、远程连接hadoop102/hadoop103/hadoop104服务器六、安装Xftp 前言 XShell远程管理工具&#xff0c;可以在Windows界面下来访…

Springboot整合RedisTemplate以及业务工具类示例

docker安装Redis参考我另一篇博客Docker安装Redis及持久化 一、Get-Started 依赖 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency><groupId>org.springframework.boot</groupId>…