当前位置: 首页 > news >正文

mybatis源码解析-sql执行流程

1 执行器的创建

1. SimpleExecutor

  • 描述:最基本的执行器,每次查询都会创建新的语句对象,并且不会缓存任何结果。

  • 特点:

    • 每次查询都会创建新的 PreparedStatement 对象。

    • 不支持一级缓存。

    • 适用于简单的查询操作,不需要缓存的情况。

2. ReuseExecutor

  • 描述:复用型执行器,会复用 PreparedStatements。

  • 特点:

    • 通过缓存 PreparedStatement 对象来提高性能。

    • 支持一级缓存。

    • 适用于多次执行相同的 SQL 语句,尤其是参数不同的情况。

3. BatchExecutor

  • 描述:批量执行器,用于批量执行 SQL 语句。

  • 特点:

    • 支持批量插入和更新操作。

    • 通过缓存 PreparedStatement 对象来提高性能。

    • 将多个 SQL 语句打包在一起,减少数据库通信次数,提高性能。

    • 适用于大数据量的批量操作。

    • 需要手动调用 flushStatements 方法来提交批量操作。

执行语句如下:

SqlSession session = sqlSessionFactory.openSession();

核心类:DefaultSqlSessionFactory 执行方法:openSession


public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {//1 获取环境变量final Environment environment = configuration.getEnvironment();//2 获取事务工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//3 创建一个sql执行器对象 默认创建SimpleExecutorfinal Executor executor = configuration.newExecutor(tx, execType);//4 创建返回一个DefaultSqlSession对象返回return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}

2 代理对象的创建

语句如下

UserMapper mapper = session.getMapper(UserMapper.class);

源码解析

核心类:DefaultSqlSession 核心方法:getMapper

public <T> T getMapper(Class<T> type) {return configuration.getMapper(type, this);
}public <T> T getMapper(Class<T> type, SqlSession sqlSession) {//1 直接去缓存knownMappers中通过Mapper的class类型去找我们的mapperProxyFactory//xml解析时 会把所有的mapper接口存放至这个map value是一个代理final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);//2 缓存中没有获取到 直接抛出异常if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {//3 通过MapperProxyFactory来创建我们的实例return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}
}public T newInstance(SqlSession sqlSession) {//1 创建我们的代理对象final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);//2 创建我们的Mapper代理对象返回return newInstance(mapperProxy);
}
//JDK代理生成对象
protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
3 sql的执行

执行语句:

User user = mapper.selectById(1);

执行这条语句时,由于mapper是一个代理对象,会自动执行创建代理对象中构造函数的InvocationHandler中invoke方法,这里mybatis包装了InvocationHandler对象,自定义了一个类继承InvocationHandler。

核心类:MapperProxy 核心方法:invoke


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//1 判断我们的方法是不是我们的Object类定义的方法,若是直接通过反射调用if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (method.isDefault()) {   //是否接口的默认方法//2 调用我们的接口中的默认方法return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}//3 真正的进行调用,做了二个事情//第一步:把我们的方法对象封装成一个MapperMethod对象(带有缓存作用的)final MapperMethod mapperMethod = cachedMapperMethod(method);//4 通过sqlSessionTemplate来调用我们的目标方法return mapperMethod.execute(sqlSession, args);
}public Object execute(SqlSession sqlSession, Object[] args) {Object result;//1 判断我们执行sql命令的类型switch (command.getType()) {//insert操作case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}//update操作case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}//delete操作case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}//select操作case SELECT://返回值为空if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {//返回值是一个Listresult = executeForMany(sqlSession, args);} else if (method.returnsMap()) {//返回值是一个mapresult = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {//返回游标result = executeForCursor(sqlSession, args);} else {//查询返回单个// 解析我们的参数Object param = method.convertArgsToSqlCommandParam(args);// 通过调用DefaultSqlSession来执行我们的sqlresult = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;
}public <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.//1 这里selectOne调用也是调用selectList方法List<T> list = this.selectList(statement, parameter);//2 若查询出来有且有一个一个对象,直接返回要给if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}
}public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {//1 第一步:通过我们的statement去我们的全局配置类中获取MappedStatementMappedStatement ms = configuration.getMappedStatement(statement);//2 通过执行器去执行我们的sql对象 默认实现:CachingExecutorreturn executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {/*** 判断我们我们的mapper中是否开启了二级缓存<cache></cache>*/Cache cache = ms.getCache();/*** 判断是否配置了<cache></cache>*/if (cache != null) {//判断是否需要刷新缓存flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);/*** 先去二级缓存中获取*/@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);/*** 二级缓存中没有获取到*/if (list == null) {//去一级缓存获取 实现类:BaseExecutorlist = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);//加入到二级缓存中tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}//没有整合二级缓存,直接去查询return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());//1 已经关闭,则抛出 ExecutorException 异常if (closed) {throw new ExecutorException("Executor was closed.");}//2 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {//3 从一级缓存中,获取查询结果queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//4 获取到,则进行处理if (list != null) {//5 处理存过的handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {//6 获得不到,则从数据库中查询list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;
}private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;
}private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {//选择配置的执行器执行sql对象 默认是SimpleExecutorlist = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;
}public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);//1 拿到连接 预处理语句statement 默认实现:PreparedStatementHandlerstmt = prepareStatement(handler, ms.getStatementLog());return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}
}public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;//1 执行sql语句ps.execute();//2 处理返回结果return resultSetHandler.handleResultSets(ps);
}

相关文章:

mybatis源码解析-sql执行流程

1 执行器的创建 1. SimpleExecutor 描述&#xff1a;最基本的执行器&#xff0c;每次查询都会创建新的语句对象&#xff0c;并且不会缓存任何结果。 特点&#xff1a; 每次查询都会创建新的 PreparedStatement 对象。 不支持一级缓存。 适用于简单的查询操作&#xff0c;不…...

Golang | Leetcode Golang题解之第538题把二叉搜索树转换为累加树

题目&#xff1a; 题解&#xff1a; func getSuccessor(node *TreeNode) *TreeNode {succ : node.Rightfor succ.Left ! nil && succ.Left ! node {succ succ.Left}return succ }func convertBST(root *TreeNode) *TreeNode {sum : 0node : rootfor node ! nil {if n…...

【linux】HTTPS 协议原理

1. 了解 HTTPS 协议原理 &#xff08;一&#xff09;认识 HTTPS HTTPS 也是一种应用层协议&#xff0c;是在 HTTP 协议的基础上引入了一个加密层 因为 HTTP协议的内容都是按照文本的方式进行传输的&#xff0c;这个过程中&#xff0c;可能会出现一些篡改的情况 &#xff08;…...

安利一款开源企业级的报表系统SpringReport

SpringReport是一款企业级的报表系统&#xff0c;支持在线设计报表&#xff0c;并绑定动态数据源&#xff0c;无需写代码即可快速生成想要的报表&#xff0c;可以支持excel报表和word报表两种格式&#xff0c;同时还可以支持excel多人协同编辑&#xff0c;后续考虑实现大屏设计…...

数据安全-接口数据混合加密笔记

接口数据传输安全设计方案 采用非对称加密对称加密混合方式&#xff0c;接口混合加、解密过程梳理&#xff1a; 后端准备sm2公钥和私钥后端将SM2公钥传输到前端前端生成SM4密钥前端使用SM2公钥加密SM4秘钥&#xff0c;获得密文使用SM4秘钥加密数据将密文和加密数据传输至后端…...

JeecgBoot入门

最近在了解低代码平台&#xff0c;其中关注到gitee上开源项目JeecgBoot&#xff0c;JeecgBoot官方也有比较完整的入门教学文档&#xff0c;这里我们将耕者官方教程学习&#xff0c;并将其记录下来。 一、项目简介 JeecgBoot 是一款基于代码生成器的低代码开发平台拥有零代码能力…...

用 Vue.js 打造炫酷的动态数字画廊:展示学生作品的创意之旅

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

【YOLO学习】YOLOv8改进举例

文章目录 1. ODConv1.1 修改1.2 原yaml文件1.3 修改yaml文件样式11.4 修改yaml文件样式2 2. DAT3. 在train下修改模型 1. ODConv 1.1 修改 1. 在ultralytics/nn/models里创建ODConv.py文件。 2. 在ultralytics/nn/task.py中导入from .modules.ODConv import C2f_ODConv,ODConv…...

文心一言 VS 讯飞星火 VS chatgpt (383)-- 算法导论24.5 3题

三、对引理 24.10 的证明进行改善&#xff0c;使其可以处理最短路径权重为 ∞ ∞ ∞ 和 − ∞ -∞ −∞ 的情况。引理 24.10(三角不等式)的内容是&#xff1a;设 G ( V , E ) G(V,E) G(V,E) 为一个带权重的有向图&#xff0c;其权重函数由 w : E → R w:E→R w:E→R 给出&…...

【AIGC】如何通过ChatGPT轻松制作个性化GPTs应用

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;前言&#x1f4af;什么是GPTsGPTs的工作原理GPTs的优势GPTs的应用前景总结 &#x1f4af;创建GPTS应用的基本流程进入GPTs创建界面方式一&#xff1a;按照引导完成生成创建GPTs方式二…...

gulp入门教程2:gulp发展历史

早期阶段&#xff08;2013年-2014年&#xff09; 诞生背景&#xff1a;随着前端开发复杂度的增加&#xff0c;开发者们开始寻求自动化工具来简化构建流程。2013年&#xff0c;由Fractal Innovations的Eric Schoffstall首次发布。它借鉴了Unix管道的流式处理思想&#xff0c;通…...

【实验八】前馈神经网络(4)优化问题

1 参数初始化 模型构建 模型训练 优化 完整代码 2 梯度消失问题 模型构建 模型训练 完整代码 3 死亡Relu问题 模型构建 模型训练 优化 完整代码 1 参数初始化 实现一个神经网络前&#xff0c;需要先初始化模型参数。如果对每一层的权重和偏置都用0初始化&#xff0…...

【深度学习】论文笔记:空间变换网络(Spatial Transformer Networks)

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a; 【机器学习】有监督学习由浅入深讲解分类算法Fisher算法讲解每日一言&#x1f33c;: 今天不想跑&#xff0c;所以才去跑&#xff0c;这才是长…...

Charles抓包_Android

1.下载地址 2.破解方法 3.安卓调试办法 查看官方文档&#xff0c;Android N之后抓包要声明App可用User目录下的CA证书 3.1.在Proxy下进行以下设置&#xff08;路径Proxy->Proxy Settings&#xff09; 3.1.1.不抓包Windows&#xff0c;即不勾选此项&#xff0c;免得打输出不…...

【MATLAB源码-第204期】基于matlab的语音降噪算法对比仿真,谱减法、维纳滤波法、自适应滤波法;参数可调。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 语音降噪技术的目的是改善语音信号的质量&#xff0c;通过减少或消除背景噪声&#xff0c;使得语音更清晰&#xff0c;便于听者理解或进一步的语音处理任务&#xff0c;如语音识别和语音通讯。在许多实际应用中&#xff0c;如…...

Scala的包及其导入

//1.单个导入 //import com.sala02.A //import com.sala02.B //2.导入多个类 //import com.sala02.{A,B} //3.导入一个包下的所有类&#xff1a;包名._ //import com.sala02._ //4.导入一个包中的类&#xff0c;给他改个名字 //格式&#xff1a;import 包名.{原来的名字 > 新…...

deepfm模型实现招聘职位推荐算法

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…...

编程之路:蓝桥杯备赛指南

文章目录 一、蓝桥杯的起源与发展二、比赛的目的与意义三、比赛内容与形式四、比赛前的准备五、获奖与激励六、蓝桥杯的影响力七、蓝桥杯比赛注意事项详解使用Dev-C的注意事项 一、蓝桥杯的起源与发展 蓝桥杯全国软件和信息技术专业人才大赛&#xff0c;简称蓝桥杯&#xff0c…...

Android 15 在状态栏时间中显示秒数

这是更新后的博客草稿,关于在Android 15状态栏中显示秒数的实现: 在Android 15状态栏中显示秒数 在Android 15中,您可以通过两种方式在状态栏中显示秒数:使用ADB命令或修改系统源代码。下面详细介绍这两种方法。 方法一:通过ADB实现 您可以使用ADB(Android调试桥)命令…...

Flutter 鸿蒙next版本:自定义对话框与表单验证的动态反馈与错误处理

在现代移动应用开发中&#xff0c;用户体验是至关重要的一环。Flutter和鸿蒙操作系统&#xff08;HarmonyOS&#xff09;的结合&#xff0c;为开发者提供了一个强大的平台&#xff0c;以创建跨平台、高性能的应用程序。本文将探讨如何在Flutter与鸿蒙next版本中创建自定义对话框…...

Unreal Engine5中使用 Lyra框架

UE5系列文章目录 文章目录 UE5系列文章目录前言一、Lyra和AIS框架的区别二、下载官方Lyra游戏示例三、Lyra在动画蓝图中的使用 前言 Unreal Engine 5&#xff08;UE5&#xff09;提供了多种用于游戏开发的模板和框架&#xff0c;其中Lyra和AlS是两个不同的示例项目&#xff0c…...

Spring Security-02-Spring Security认证方式-HTTP基本认证、Form表单认证、HTTP摘要认证、前后端分离安全处理方案

Lison <dreamlison163.com>, v1.0.0, 2024.06.01 Spring Security-02-Spring Security认证方式-HTTP基本认证、Form表单认证、HTTP摘要认证、前后端分离安全处理方案 文章目录 Spring Security-02-Spring Security认证方式-HTTP基本认证、Form表单认证、HTTP摘要认证、…...

【scikit-learn 1.2版本后】sklearn.datasets中load_boston报错 使用 fetch_openml 函数来加载波士顿房价

ImportError: load_boston has been removed from scikit-learn since version 1.2. 由于 load_boston 已经在 scikit-learn 1.2 版本中被移除&#xff0c;需要使用 fetch_openml 函数来加载波士顿房价数据集。 # 导入sklearn数据集模块 from sklearn import datasets # 导入波…...

vxe-table v4.8+ 与 v3.10+ 导出 xlsx、支持导出合并、设置样式、宽高、边框、字体、背景、超链接、图片的详细介绍,一篇就够了

Vxe UI vue vxe-table v4.8 与 v3.10 导出 xlsx、支持导出合并、设置样式、宽高、边框、字体、背景、超链接、图片等、所有常用的 Excel 格式都能自定义&#xff0c;使用非常简单&#xff0c;纯前端实现复杂的导出。 安装插件 npm install vxe-pc-ui4.2.39 vxe-table4.8.0 vx…...

江协科技STM32学习- P36 SPI通信外设

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…...

【大数据】ClickHouse常见的表引擎及建表语法

ClickHouse 中最强大的表引擎当属 MergeTree &#xff08;合并树&#xff09;引擎及该系列&#xff08;*MergeTree&#xff09;中的其他引擎。接下来我们就仔细了解下MergeTree 及该系列的其他引擎的使用场景及建表语法。 MergeTree MergeTree 系列的引擎被设计用于插入极大量…...

explain执行计划分析 ref_

这里写目录标题 什么是ExplainExplain命令扩展explain extendedexplain partitions 两点重要提示本文示例使用的数据库表Explain命令(关键字)explain简单示例explain结果列说明【id列】【select_type列】【table列】【type列】 【possible_keys列】【key列】【key_len列】【ref…...

网络学习/复习4传输层

1,0...

Notepad++ 更改字体大小和颜色

前言 在长时间编程或文本编辑过程中&#xff0c;合适的字体大小和颜色可以显著提高工作效率和减少眼睛疲劳。Notepad 提供了丰富的自定义选项&#xff0c;让你可以根据个人喜好调整编辑器的外观。 步骤详解 1. 更改字体大小 打开 Notepad 启动 Notepad 编辑器。 进入设置菜…...

基于SSM+小程序的宿舍管理系统(宿舍1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 本宿舍管理系统小程序有管理员和学生两个角色。 1、管理员功能有个人中心&#xff0c;公告信息管理&#xff0c;班级管理&#xff0c;学生管理&#xff0c;宿舍信息管理&#xff0c;宿舍…...