【问题处理】—— Mybatis缓存可改导致的异常

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【问题处理】—— Mybatis缓存可改导致的异常

现象:数组越界

        List<Integer> dateList = commonMapper.queryDateBefore(count);
        dateList.remove(0);
        return dateList.get(dateList.size() - 1);

报的是数组越界,错误下标是-1;然而我们从日志看commonMapper.queryDateBefore 返回的结果是8条;8条去掉1条,怎么会变成0了呢?


分析:缓存与返回同对象

一开始百思不得其解,翻遍日志,可以看到所有调用commonMapper.queryDateBefore的返回都是8条,而这里的List dateList是个局部变量,也不会有多线程来干扰


甚至看了remove()的源码,当然结果一无所获


直到后面打了断点,发现这个地方会执行多次,突然想到会不会是myBatis缓存问题,我们看到的日志是8没错,但是同一个线程多次执行此Sql,我印象中后续是会直接返回缓存,而不是再查数据库的,那myBatis自然不会再打印查询日志了,所以造成了所有查询的结果都是8条。


这其实有点让人意外,因为我一直认为myBatis的缓存是一种备份形式,没想到其缓存和返回值就是同一个对象,对查出来的数据进行修改后,再次查询其原值居然是错的


为了验证这种猜测,找到了缓存部分的源码:org.apache.ibatis.executor.BaseExecutor

48c8746c65954b8d902d20c1ba6ea4d9.png

果然,在查询list的时候,会把该list存储在缓存中再返回,如果我们对返回值进行操作,那实际也就是在操作缓存内容,导致本线程第二次查询时,得到错误的数据


结果:拷贝后修改

在查询出结果后,不直接对原list进行操作,而是对list采用一次部分深拷贝,然后再操作新集合

        List<Integer> dateList = commonMapper.queryDateBefore(count);
        ArrayList<Object> newDateList = new ArrayList<>(dateList);
        newDateList.remove(0);
        return newDateList.get(newDateList.size() - 1);
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
22天前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
27 1
|
26天前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
41 4
|
1月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
1月前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
82 5
|
2月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
3月前
|
SQL Java 数据库连接
Mybatis的Cursor如何避免OOM异常
在 Mybatis 中,`Cursor` 是一个特殊对象,用于避免大量数据查询时导致的 OOM 错误。它通过懒加载和迭代器实现内存友好型数据处理,尤其适用于大规模数据查询。使用时只需将 Mapper 文件中的方法返回值设为 `Cursor&lt;T&gt;`。其原理在于操作原生 `Statement` 并按需获取数据,而非一次性加载所有数据,从而避免内存溢出。
136 3
|
4月前
|
缓存 NoSQL Java
【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
|
4月前
|
缓存 NoSQL 网络协议
【Azure Redis 缓存】Redisson 连接 Azure Redis出现间歇性 java.net.UnknownHostException 异常
【Azure Redis 缓存】Redisson 连接 Azure Redis出现间歇性 java.net.UnknownHostException 异常
120 1
|
5月前
|
缓存 算法 Java
关于MyBatis的缓存详解
MyBatis 的缓存机制非常灵活,可以通过简单的配置来满足不同的性能需求。合理地使用缓存可以显著提高应用程序的性能,尤其是在处理大量数据库查询时。然而,开发者需要注意缓存的一致性和并发问题,特别是在使用可读写缓存时。