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

news/2024/10/17 6:23:50/

目录

验证代码如下

mappper 代码

xml 中代码

实际执行代码

执行结果

DefaultSqlSession

CachingExecutor

BaseExecutor

PerpetualCache

总结

禁用一级缓存

mapper 对应的 xml 的 select 查询设置 flushCache 属性为 true

MappedStatement 的内部类 Builder 向外部变量 flushCacheRequired 赋值

MapperBuilderAssistant 的 setStatementCache()

XMLStatementBuilder 的 parseStatementNode() 解析 mapper 中定义的 sql

mybatis 全局配置 settings 中添加 name 为 localCacheScope 的节点,对应 value 为 STATEMENT

XMLConfigBuilder的settingsElement()方法

有一个问题,为什么每次创建 SqlSession 缓存就不能共用了?


在之前的文章基础上

https://blog.csdn.net/zlpzlpzyd/article/details/135171524

验证代码如下

mappper 代码

package cn.hahaou.mybatis.cache.levelone.mapper;import cn.hahaou.mybatis.cache.levelone.entity.Role;public interface LevelOneRoleMapper {Role getRole(Long id);
}

xml 中代码

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.hahaou.mybatis.cache.levelone.mapper.LevelOneRoleMapper"><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.levelone;import cn.hahaou.mybatis.cache.levelone.mapper.LevelOneRoleMapper;
import cn.hahaou.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;/*** 一级缓存测试*/
public class LevelOneCacheTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.openSession();LevelOneRoleMapper roleMapper = sqlSession.getMapper(LevelOneRoleMapper.class);roleMapper.getRole(1L);System.out.println("使用同一个SqlSession再执行一次");roleMapper.getRole(1L);sqlSession.close();}
}

执行结果

2023-12-23 19:30:56,109 [main] DEBUG org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
2023-12-23 19:30:56,204 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:30:56,206 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:30:56,206 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:30:56,206 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:31:15,482 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
2023-12-23 19:31:15,943 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 439928219.
2023-12-23 19:31:15,943 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1a38c59b]
2023-12-23 19:31:15,953 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from t_role t where t.id = ? 
2023-12-23 19:31:16,082 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
2023-12-23 19:31:16,187 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
使用同一个SqlSession再执行一次
2023-12-23 19:32:41,371 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1a38c59b]
2023-12-23 19:32:41,378 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1a38c59b]
2023-12-23 19:32:41,379 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 439928219 to pool.

可以看到,最终查询只执行了一次。

在之前的获取 SqlSession 的基础上,有一个地方

这里的 Executor 是在  Configuration 中获取的,这里需要一个参数 ExecutorType,看看调用这里的地方是怎么传参的。

可知,默认是取的  Configuration 的 defaultExecutorType 的值,再到 Configuration 看一下。

由源码得知,Configuration 中 ExecutorType 的值默认为 SIMPLE

 回到刚才创建的地方,最终返回的结果为 CachingExecutor,通过装饰器模式的方式包装了 SimpleExecutor。

Executor的实现类

DefaultSqlSession

调用了 mapper 中的方法后,触发反射操作进入 MapperProxy 的 invoke()

鉴于当前查询是单值操作,走查询单条数据逻辑

MapperMethod 中有两个类型的变量 SqlCommand 和 MethodSignature。

SqlCommand 里面有两个变量

name 保存了当前访问的 mapper 方法。

type 对应 mapper 里的 sql 标签定义的各种 sql 操作类型,即 dml 操作

MethodSignature

对应 mapper 里的 sql 标签里的各个属性设置

 

通过 Configuration 获取 MappedStatement 对象。

MappedStatement 中 sqlSource 的类型是 RawSqlSource,通过装饰器模式的方式内嵌了 StaticSqlSource,最终的 sql 在变量 sql 里。

调用 CachingExecutor 的 query()

CachingExecutor

在这里有两件事,创建 CacheKey 和调用嵌套的 SimpleExecutor 执行查询。其中 CacheKey 用于接下来缓存查询结果使用。

BaseExecutor

SimpleExecutor 是 BaseExecutor 的子类。CachingExecutor 中调用了 createCacheKey() 实际上调用了 CacheKey 的 createCacheKey()。

其中主要通过 update() 向里面的集合变量添加数据。具体信息如下

第1个为 MappedStatement 的 id,即需要查询的方法全路径
第2个为 RowBounds 的 offset,默认值为 0
第3个为 RowBounds 的 limit,默认值为 Integer.MAX_VALUE
第4个为 BoundSql 的 sql
第5个为查询的参数值
第6个为 Environment 信息,在配置中指定

定义了一个全局变量 localCache 缓存查询结果

首次查询没有数据,调用 queryFromDatabase() 从数据库中查询。否则,直接从缓存中获取数据。

 查询结束后将 CacheKey 和对应的结果分为作为键值对保存到 localCache 中。

PerpetualCache

数据最终存储到了 map 对象中。

id 对应的是字符串 LocalCache

map 变量 cache 中,key 对应的是 CacheKey,value 对应的是返回的查询结果。

总结

可以看到,通过一个简单的查询,mybatis 使用了装饰器模式实现了一级缓存,默认启用,通过内存缓存当前查询结果,但是这个只适用于那种单体应用。

项目使用了集群部署的话使用一级缓存不太好,有缓存不一致的问题。如果数据量大的话会造成内存溢出的情况发生。

所以,针对项目部署的是集群环境,不要用一级缓存。如果是单体数据量不大可以使用。

鉴于一级缓存的执行逻辑在 BaseExecutor,所以二级缓存的全局启用设置 cacheEnabled 是否为 true 对于一级缓存没作用。

禁用一级缓存

可知,有两种方式

mapper 对应的 xml 的 select 查询设置 flushCache 属性为 true

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

MappedStatement 的内部类 Builder 向外部变量 flushCacheRequired 赋值

MapperBuilderAssistant 的 setStatementCache()

setStatementCache() 的上层调用方法 addMappedStatement()

XMLStatementBuilder 的 parseStatementNode() 解析 mapper 中定义的 sql

可以看到,如果 SqlCommandType 值为 SELECT,flushCache 的值的情况如下

如果 flushCache 的值未设置,flushCache 值为 false,默认使用缓存。

如果 flushCache 的值设置为 true,flushCache 值为 true,禁止使用缓存。

mybatis 全局配置 settings 中添加 name 为 localCacheScope 的节点,对应 value 为 STATEMENT

<setting name="localCacheScope" value="STATEMENT"/>

其中 localCacheScope 实际对应的是枚举类型 LocalCacheScope,只有两个值,默认值为 SESSION。

XMLConfigBuilder的settingsElement()方法

package org.apache.ibatis.session;/*** @author Eduardo Macarron*/
public enum LocalCacheScope {SESSION,STATEMENT
}

如果指定了其他值,创建 SqlSessionFactory 的过程中会出现异常。

这两种方式,要讲哪种方式好,还是全局方式好,当然,对于一级缓存禁用的情况需要按照实际情况来。

有一个问题,为什么每次创建 SqlSession 缓存就不能共用了?

因为每次在调用 SqlSessionFactory 的 openSession() 都会创建 Executor 实例,但是 BaseExecutor 是 SimpleExecutor 的父类,CachingExecutor 通过装饰器模式包装了 SimpleExecutor,所以,新建了 SqlSession 就不能使用之前的缓存了。


http://www.ppmy.cn/news/1283293.html

相关文章

SpringMVC:整合 SSM 上篇

文章目录 SpringMVC - 03整合 SSM 上篇一、准备工作二、MyBatis 层1. dao 层2. service 层 三、Spring 层四、SpringMVC 层五、执行六、说明 SpringMVC - 03 整合 SSM 上篇 用到的环境&#xff1a; IDEA 2019&#xff08;JDK 1.8&#xff09;MySQL 8.0.31Tomcat 8.5.85Maven…

list集合

List集合 List集合的概述 有序集合&#xff08;也称之为序列&#xff09;&#xff0c;用户可以精确的控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素&#xff0c;并搜索列表中的元素 与 Set 集合不同&#xff0c;列表通常允许重复的元素 List 集合的特点 有…

7.7、kali linux环境下搭建DVWA

目录 一、资料下载准备工作 1.1、DVWA源代码下载 二、开启Apache、mysql服务 2.1、下载Apache2文件 2.2、开启Apache2服务 方法一&#xff1a;开启Apache2服务&#xff08;手动&#xff09; 方法二&#xff1a;开启Apache2服务&#xff08;系统自启动&#xff09; 2.3、…

【AI】人工智能本地环境集成安装

目录 1、基础安装 1.1 GPU安装 1.1.1 GPU版本支持 1.1.2 下载CUDA 1.1.3安装CUDA 1.1.4配置环境变量 1.1.5检测CUDA是否安装成功 1.2 CUDNN安装 1.2.1 下载CUDNN 1.2.2 添加配置 1.2.3验证结果 2、pytorch安装

centos 安装oracle 11.2.04 并配置数据库自启动操作记录,一次完成

环境&#xff1a; centos版本7.3&#xff0c;安装的有图形化界面 Oracle11.2.04&#xff0c;之所以选择这个版本是因为网上有人说11其他版本的在安装的过程中会出现这样或那样的问题&#xff0c;下载地址放到文章下面 步骤&#xff0c;按顺序&#xff1a; 1、创建安装Oracle…

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

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

云计算:现代技术的基本要素

众所周知&#xff0c;在儿童教育的早期阶段&#xff0c;幼儿园都会传授塑造未来行为的一些基本准则。 今天&#xff0c;我们可以以类似的方式思考云计算&#xff1a;它已成为现代技术架构中的基本元素。云现在在数字交互、安全和基础设施开发中发挥着关键作用。云不仅仅是另一…

对象属性的重命名

一、对象解构进行对象属性的重命名 const obj { oldName: value };const { oldName: newName } obj; console.log(newName); // 输出: value console.log(obj.oldName) //value console.log(obj) // { oldName: value } 注意&#xff0c;这种重命名方式不会直接修改原…