mybatis的二级缓存使用以及禁用

目录

mybatis 二级缓存配置有两处

全局设置

mapper 设置

测试代码

执行结果

源码执行逻辑

创建 SqlSession

二级缓存配置是否添加

解析 cache 标签

XMLMapperBuilder

MapperBuilderAssistant

CacheBuilder

PerpetualCache

SerializedCache

LoggingCache

将 cache 对象添加到 MappedStatement 对象中

XMLStatementBuilder

MapperBuilderAssistant

查询数据

调用 CachingExecutor 的 query()

调用 TransactionalCacheManager 的 getObject()

调用 SqlSession 的 commit()

调用 CachingExecutor 的 commit()

调用 TransactionalCacheManager 的 commit()

调用 TransactionalCache 的 commit()

缓存失效

执行非 select 操作

XMLStatementBuilder

在 mapper 的 select 标签中设置 flushCache 为 true

CachingExecutor

缓存禁用

xml 中去掉  标签

全局配置

总结


在之前写的 mybatis 文章基础上

mybatis的生命周期-CSDN博客

mybatis的一级缓存使用以及禁用-CSDN博客

mybatis 二级缓存配置有两处

两个一起设置才能生效

全局设置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 配置文件上下文使用的属性值引用外部文件 -->
    <properties resource="jdbc.properties"></properties>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 默认启用二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!-- 定义别名,在 mapper 的返回类型中使用 -->
    <typeAliases>
        <typeAlias type="cn.hahaou.mybatis.cache.leveltwo.entity.Role" alias="role"/>
    </typeAliases>

    <!-- 定义数据库信息,默认使用 development 数据库构建环境 -->
    <environments default="test">
        <environment id="test">
            <!-- 采用 jdbc 事务管理 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据库连接信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 定义映射器 -->
    <mappers>
        <mapper class="cn.hahaou.mybatis.cache.leveltwo.mapper.LevelTwoRoleMapper"/>
    </mappers>
</configuration>

mapper 设置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.hahaou.mybatis.cache.leveltwo.mapper.LevelTwoRoleMapper">

    <!-- 启用二级缓存标签 -->
    <cache />

    <resultMap id="roleMap" type="role">
        <id column="id" property="id" javaType="long" jdbcType="BIGINT"></id>
        <result column="role_name" property="roleName" javaType="string" jdbcType="VARCHAR"></result>
        <result column="note" property="note"></result>
    </resultMap>

    <select id="getRole" parameterType="long" resultType="role">
        select * from t_role t where t.id = #{id}
    </select>
</mapper>

测试代码

package cn.hahaou.mybatis.cache.leveltwo;

import cn.hahaou.mybatis.cache.leveltwo.mapper.LevelTwoRoleMapper;
import cn.hahaou.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;

/**
 * 二级缓存测试
 */
public class LevelTwoCacheTest {

    public static void main(String[] args) {
        {
            try (SqlSession sqlSession = MybatisUtils.openSession()){
                LevelTwoRoleMapper roleMapper = sqlSession.getMapper(LevelTwoRoleMapper.class);
                roleMapper.getRole(1L);
                sqlSession.commit();
            }
        }
        System.out.println("开启新session查询");
        {
            try (SqlSession sqlSession = MybatisUtils.openSession()){
                LevelTwoRoleMapper roleMapper = sqlSession.getMapper(LevelTwoRoleMapper.class);
                roleMapper.getRole(1L);
                sqlSession.commit();
            }
        }
    }
}

执行结果

2023-12-24 16:32:10,019 [main] DEBUG org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
2023-12-24 16:32:10,100 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-24 16:32:10,101 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-24 16:32:10,101 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-24 16:32:10,101 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-24 16:32:10,148 [main] DEBUG org.apache.ibatis.cache.decorators.LoggingCache: Cache Hit Ratio [cn.hahaou.mybatis.cache.leveltwo.mapper.LevelTwoRoleMapper]: 0.0
2023-12-24 16:32:10,150 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
2023-12-24 16:32:10,347 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 510109769.
2023-12-24 16:32:10,348 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1e67a849]
2023-12-24 16:32:10,355 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from t_role t where t.id = ? 
2023-12-24 16:32:10,374 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
2023-12-24 16:32:10,395 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
2023-12-24 16:32:10,401 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1e67a849]
2023-12-24 16:32:10,407 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1e67a849]
2023-12-24 16:32:10,407 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 510109769 to pool.
开启新session查询
2023-12-24 16:32:10,409 [main] DEBUG org.apache.ibatis.cache.decorators.LoggingCache: Cache Hit Ratio [cn.hahaou.mybatis.cache.leveltwo.mapper.LevelTwoRoleMapper]: 0.5

从日志可以得知

第一个 session 查询缓存命中比率为 0,执行 jdbc 查询。

第一个 session 查询缓存命中比率为 0.5,不执行 jdbc 查询。

源码执行逻辑

创建 SqlSession

先通过 Configuration 创建 Executor

Configuration 中的变量 cacheEnabled 对应的值是全局配置的 cacheEnabled 的值,如果未指定,默认值为 true。最终结果返回了一个 CachingExecutor,通过装饰器模式封装了一个 SimpleExecutor。

二级缓存配置是否添加

其中,参数 MappedStatement 对应的是每个 mapper 中定义的用于执行 sql 操作的方法。获取其中的 cache 对象判断是否为空,不为空执行二级缓存逻辑。

解析 cache 标签

XMLMapperBuilder

在 XMLMapperBuilder 中解析 mapper 中定义的 cache 标签。

通过代码得知如下

type 未指定,默认值为 PERPETUAL,对应的是 PerpetualCache,与一级缓存一致。

eviction 未指定,默认值为 LRU,对应的是 LruCache,即缓存淘汰策略使用了 lru 算法。

readOnly 未指定,默认值为 false,但是又对结果进行了取反,所以结果是 true。对用后面的 SerializedCache。

MapperBuilderAssistant

调用 useNewCache() 通过 CacheBuilder 链式编程创建 Cache 对象。

设置 Configuration 的缓存对象为链式创建的。

CacheBuilder

通过 setDefaultImplementations() 得知,如果在调用 useNewCache() 创建 Cache 对象时变量typeClass 和 evictionClass 未指定,分别指定 PerpetualCache 和 LruCache。

在 build() 中循环当前类的 decorators 对象,对 PerpetualCache 使用装饰器模式包装。第一层为 LruCache。

在 setStandardDecorators() 对传入的 Cache 对象(实际为 PerpetualCache)进行装饰器模式包装处理。

鉴于是默认处理,所有最终的 cache 对象如下,中间通过内部变量 delegate 来进行引用。

SynchronizedCache
↓
LoggingCache 计算命中率
↓
SerializedCache 需要缓存的数据需要序列化
↓
LruCache 缓存淘汰策略
↓
PerpetualCache 最终保存缓存的对象

PerpetualCache

最终存储的数据如下

id 为 mapper 全路径。一级缓存是常量字符串。

map 变量 cache 中,key 对应的是 CacheKey,value 对应的是返回的查询结果序列化后的字节数组。一级缓存是返回结果没做任何处理。

 鉴于最终存储的结果是序列化后的字节数组,所以返回的对象需要实现序列化接口 Serializable。

SerializedCache

LoggingCache

针对每次查询请求对变量值进行计数累加,如果查询的数据在缓存中存在,命中数进行累加。

 

命中率计算=命中数/请求总数。

将 cache 对象添加到 MappedStatement 对象中

在 XMLMapperBuilder 中间接调用 XMLStatementBuilder 进行 crud sql 解析处理

XMLStatementBuilder

根据当前查询类型为 select,默认启用缓存。

看到这里感觉很熟悉,因为上面的 flushCache 是一级缓存的标识。

MapperBuilderAssistant

调用 addMappedStatement() 添加到 Configuration  中。

设置 MappedStatement 中二级缓存相关相关的变量

useCache 表示启用二级缓存
cache 表示缓存对象

通过链式编程方式赋值最终构建 MappedStatement 对象。

查询数据

通过 TransactionalCacheManager 这个中间对象来完成。

调用 CachingExecutor 的 query()

在上图中,从 MappedStatement 中获取 cache 对象和 useCache 的值来判断是否启用二级缓存,从上面的分析可以得出,这里 useCache 的判断用于判断当前 statement 的 SqlCommandType 的值,如果为 SELECT 就启用,否则不启用。

调用 TransactionalCacheManager 的 getObject()

通过 getObject() 传入 cache 对象判断对应的 TransactionalCache 在  TransactionalCacheManager 中是否存在

通过 cache 对象判断在 map 对象 transactionalCaches 中是否存在,不存在使用 TransactionalCache 包装添加到 transactionalCaches 中。

因为使用的装饰器模式,最终调用的是 PerpetualCache 中的 cache 变量。

通过 putObject() 传入 cache 对象判断对应的 TransactionalCache 在  TransactionalCacheManager 中是否存在,存在添加到 TransactionalCache 的 entriesToAddOnCommit 变量中。

调用 SqlSession 的 commit()

间接调用 CachingExecutor 的 commit()。

调用 CachingExecutor 的 commit()

调用 TransactionalCacheManager 的 commit()

对于  transactionalCaches 中的 value 值调用对应的 commit() 方法。

调用 TransactionalCache 的 commit()

间接调用 flushPendingEntries()

对应上面调用 putObject() 时向 entriesToAddOnCommit 中添加,因为使用的装饰器模式,最终添加到 PerpetualCache 中的 cache 变量。

其中,对于二级缓存调用 SqlSession 的 close() 也可以做到,对于忘记提交的情况,这里体现了补救措施。

 

上面的测试代码使用了 java 7 中带的 try-with-resources 功能,括号中的变量只能是 Closeable 或者 AutoCloseable 的实现类,在编译器编译时自动添加 close() 防止流关闭的情况。

java中的Closeable与AutoCloseable-CSDN博客

缓存失效

有两种方式

执行非 select 操作

即 insert、delete、update。

XMLStatementBuilder

在执行非查询操作时,useCache 的值为 false,由于该值最终在 MappedStatement 的 useCache 中进行赋值,最终在 CachingExecutor 中会对该值进行判断,如果为 false,不会走缓存的逻辑,会导致缓存失效。

在 mapper 的 select 标签中设置 flushCache 为 true

CachingExecutor

通过上面的分析得知,二级缓存添加是通过 TransactionalCacheManager 的 commit() 来完成的,所以将 flushCache 设置为 false 后,删除 TransactionalCacheManager 中 transactionalCaches 的变量值,导致添加的 TransactionalCache 被清空,所以需要再次查询数据。

缓存禁用

有两种方式

xml 中去掉 <cache /> 标签

作用是 mapper 层级

全局配置

<setting name="cacheEnabled" value="false"/>

默认值为 true,如果设置为 false,mapper 设置后将不起作用。

mybatis 中将二级缓存设置了一半(Configuration 中 cacheEnabled 值为 true),另外一半需要使用者自己处理(mapper 中需要添加 <cache /> 标签进行自定义参数处理,如果不指定参数有默认参数)。

其实二级缓存最终通过 CachingExecutor 来实现,如下图,全局配置在这里进行逻辑处理,如果没配置相关的,则在创建 SqlSession 时最终返回的是 SimpleExecutor。

总结

一级缓存针对的是 SqlSession 层次,二级缓存针对的是 mapper 层次。

最终的缓存类都是 PerpetualCache,只是二级缓存通过装饰器模式串联了多个 cache 实现,可以针对不同的功能串联不同的 cache 实现。

一级缓存和二级缓存都有一个缺点,无法解决缓存共用的问题。所以,针对集群项目不建议使用一级缓存和二级缓存,最好禁用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/270437.html

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

相关文章

电商数据分析-01-电商数据分析指标

电商数据指标 电商数据分析涉及多个指标&#xff0c;这些指标可以帮助企业了解其业务表现、用户行为和市场趋势。以下是一些常见的电商数据分析指标&#xff1a; 销售指标&#xff1a; 总销售额&#xff08;GMV&#xff09;&#xff1a; 衡量特定时期内所有销售交易的总值。 平…

算法学习系列(十一):KMP算法

目录 引言一、算法概念二、题目描述三、思路讲解三、代码实现四、测试 引言 这个KMP算法就是怎么说呢&#xff0c;就是不管算法竞赛还是找工作笔试面试&#xff0c;都是非常爱问爱考的&#xff0c;其实也是因为这个算法比较难懂&#xff0c;其实就是很难&#xff0c;所以非常个…

【环境配置】虚拟环境配置

创建虚拟环境 conda create -n pytorch python3.9安装成功提示 激活虚拟环境 activate pytorch安装pytorch 查看 python 版本——python 退出 python——exit() 对照 python 与 pytorch 的对应关系 pytorch 地址&#xff1a; https://pytorch.org/get-started/previous-…

Python in Visual Studio Code 2023年12月发布

作者&#xff1a;Courtney Webster 排版&#xff1a;Alan Wang 我们很高兴地宣布 Visual Studio Code 的 Python 和 Jupyter 扩展将于 2023 年 12 月发布&#xff01; 此版本包括以下公告&#xff1a; 可配置的调试选项已添加到“运行”按钮菜单可以使用 Pylance 显示类型层次…

JAVA JDK8时间类之Period、Duration、ChronoUnit的使用【详解】

JAVA JDK8时间类之Period、Duration、ChronoUnit的使用 1. Duration1.1 简介&#xff1a;用于时间间隔(秒、毫秒、纳秒等)1.2 案例 2. Period时间间隔(年、月、日)2.1 简介2.2 案例 3. ChronoUnit3.1 简介案例 4. 案例所有代码&#xff1a; 1. Duration 1.1 简介&#xff1a;用…

边缘计算AI智能盒子的视频源必须是固定点监控摄像头吗?

边缘计算AI盒子的视频输入源&#xff0c;要求是RTSP或者GB28181&#xff0c;可以是固定点监控摄像头&#xff08;枪机、球机等&#xff09;&#xff0c;也可以是移动摄像头&#xff0c;例如执法记录仪、智能安全帽、布控球等&#xff0c;但由于RTSP输入要求摄像头有固定IP&…

中庸 原文与译文

《中庸》是中国古代论述人生修养境界的一部道德哲学专著&#xff0c;是儒家经典著作之一&#xff0c;原属《礼记》第三十一篇&#xff0c;相传为战国时期子思所作。 其内容肯定“中庸”是道德行为的最高标准&#xff0c;认为“至诚”则达到人生的最高境界&#xff0c;并提出“…

算法——哈希表

哈希表简介 **是什么&#xff1a;**存储数据的容器有什么用&#xff1a;快速查找某个元素&#xff0c;时间复杂度O(1)&#xff0c;空间复杂度O(n)**什么时候使用哈希表&#xff1a;**频繁查找某一个数&#xff08;这里不要忘了之前的二分&#xff0c;时间复杂度O(logN)&#x…

sqlilabs第三十二三十三关

Less-32&#xff08;GET - Bypass custom filter adding slashes to dangerous chars) 手工注入 由 宽字符注入可知payload 成功触发报错 http://192.168.21.149/Less-32/ ?id1%df 要写字符串的话直接吧字符串变成ascii码 注意16进制的表示方式 自动注入 sqlmap -u http:…

三相电机转差率为负值的情形

1.电机开始发电的特征 注意&#xff0c;电机因为有输入频率对原始旋转磁场的影响&#xff0c;在正常工作时&#xff0c;应该处于稳态&#xff0c;因为旋转磁场决定了这个系统的运转方向和运转的大致频率区间。它会处于力矩平衡态。但是&#xff0c;如果&#xff0c;此时电机处…

智能优化算法应用:基于指数分布算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于指数分布算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于指数分布算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.指数分布算法4.实验参数设定5.算法结果6.…

《Halcon 100项目-2》Halcon查找零件个数

Halcon查找零件个数 read_image (Image20231225201927, D:/image/bilibili/photo/屏幕截图 2023-12-25 201927.png) rgb1_to_gray (Image20231225201927, GrayImage)threshold (GrayImage, Region, 0, 128) draw_rectangle1 (200000, Row1, Column1, Row2, Column2) gen_recta…

redis基本用法学习(C#调用StackExchange.Redis操作redis)

StackExchange.Redis是基于C#的高性能通用redis操作客户端&#xff0c;也属于常用的redis客户端之一&#xff0c;本文学习其基本用法。   新建Winform项目&#xff0c;在Nuget包管理器中搜索并安装StackExchange.Redis&#xff0c;如下图所示&#xff1a;   StackExchange.…

ElasticSearch 使用映射定义索引结构

动态映射 dynamic 可选值解释true默认值&#xff0c;启用动态映射&#xff0c;新增的字段会添加到映射中runtime查询时动态添加到映射中false禁用动态映射&#xff0c;忽略未知字段strict发现未知字段&#xff0c;抛出异常 显示映射 创建映射 PUT user {"mappings&qu…

sql_lab之sqli注入中的cookie注入

Cookei注入&#xff08;gxa的从cookei注入&#xff09; 1.打开控制台 2.验证id2时的值 document.cookie"id2" 3.判断是上面闭合方式 document.cookie"id2 -- s" 有回显 说明是’单引号闭合 4.用order by 判断字段数 5.用联合查询判断回显点 接下来的…

C语言 指针

C语言学习&#xff01; 目录 文章目录 前言 一、指针是什么&#xff1f; 二、指针变量的大小 三、指针和指针类型 四、指针和函数 五、野指针 5.1野指针成因 5.2 如何规避野指针 六、指针运算 6.1 指针- 整数 6.2 指针-指针 6.3 指针的关系运算 总结 前言 指针理解的2个要点&a…

(2023|CVPR,Corgi,偏移扩散,参数高斯分布,弥合差距)用于文本到图像生成的偏移扩散

Shifted Diffusion for Text-to-image Generation 公众&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 1. 简介 2. 方法 2.1 偏移扩散 3. 实验 3.1 无监督文本到图像生成 3.2 无…

IDEA Maven Helper插件 解决jar冲突

Jar包冲突报错 程序抛出java.lang.ClassNotFoundException异常&#xff1b; 程序抛出java.lang.NoSuchMethodError异常&#xff1b; 程序抛出java.lang.NoClassDefFoundError异常&#xff1b; 程序抛出java.lang.LinkageError异常等&#xff1b;Maven Jar包管理机制 在Maven项…

设计模式--工厂方法模式

实验3&#xff1a;工厂方法模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解工厂方法模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用工厂方法模式解决实际问题。 [实验任务]&#xff1a;加密算法 目前常用…

数据库管理-第127期 LSM Tree(202301225)

数据库管理-第127期 LSM Tree&#xff08;202301225&#xff09; 说起分布式数据库&#xff0c;绕不开的一个话题就是LSM Tree&#xff0c;全称为log-structured merge-tree&#xff0c;回到吕海波老师授权过的那句话“没搞过Oracle的&#xff0c;但又是数据库圈里的人&#x…