關于 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.pswp.cn/web/38492.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/38492.shtml
英文地址,請注明出處:http://en.pswp.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…