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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

儿童安全座椅行业全面深入分析

儿童安全座椅就是一种专为不同体重&#xff08;或年龄段&#xff09;的儿童设计&#xff0c;将孩子束缚在安全座椅内&#xff0c;能有效提高儿童乘车安全的座椅。欧洲强制性执行标准ECE R44/03的定义是&#xff1a;能够固定到机动车辆上&#xff0c;带有ISOFIX接口、LATCH接口的…

Pytest-Bdd-Playwright 系列教程(6):在测试步骤函数中设置别名数据共享

Pytest-Bdd-Playwright 系列教程&#xff08;6&#xff09;&#xff1a;在测试步骤函数中设置别名&数据共享 前言一、步骤别名二、特性文件三、测试脚本四、运行测试五、小测验总结 前言 有的时候&#xff0c;为了提高可读性&#xff0c;我们需要使用不同的名称来声明相同的…

【笔记】变压器-热损耗-频响曲线推导 - 03 变压器参数-特性

参考《Mn-Zn开关电源用铁氧体磁心 PQ系列》&#xff0c;March 2014版 1.可能选择的型号和参数 PQ系列的这种铁氧体结构设计是TDK首创的。 优势是&#xff0c;相对E, EER磁芯&#xff0c;安装面积更小材质选择 PC47PC90PC95尺寸 PQ20/16 最小PQ50/50 最大 2.特性 2.1 温升与…

Unity性能优化 -- 性能分析工具

Stats窗口Profiler窗口Memory Profiler其他性能分析工具&#xff08;Physica Debugger 窗口&#xff0c;Import Activity 窗口&#xff0c;Code Coverage 窗口&#xff0c;Profile Analyzer 窗口&#xff0c;IMGUI Debugger 窗口&#xff09; Stats 统级数据窗口 game窗口 可…

一些swift问题

写得比较快&#xff0c;如果有问题请私信。 序列化和反序列化 反序列化的jsonString2只是给定的任意json字符串 private func p_testDecodeTable() {let arr ["recordID123456", "recordID2"]// 序列化[string] -> json datalet jsonData try? JSO…

VMware虚拟机Debian扩展磁盘

一、 版本 VMware&#xff1a;Workstation 17 Pro虚拟机&#xff1a;Debian11 二、 VMware虚拟机扩展 虚拟机关机状态快照或者备份&#xff1a;以免扩容失败导致文件丢失虚拟机——设置——硬盘——磁盘使用工具——扩展——扩展磁盘容量——设置为想要的大小 三、 虚拟机…

GameFramework教程☀️福利(五):关于该框架的一些意义

文章目录 📢 不同模式的意义本章探讨GF这样编写的意义和使用场景。 📢 不同模式的意义 最近在做一个app,现在在调研阶段。 代码上后期可能用华佗进行C#热更新。 在调研华佗打包完的热更代码如何和UI AB结合起来时,看到了: "> 从这一点可以延伸理解出,当我们使…

【漏洞复现】某平台-QRcodeBuildAction-LoginSSO-delay-mssql-sql注入漏洞

《Java代码审计》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484219&idx1&sn73564e316a4c9794019f15dd6b3ba9f6&chksmc0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene21#wechat_redirect 《Web安全》h…

Armv8的安全启动

目录 1. Trust Firmware 2. TF-A启动流程 3. TF-M启动流程 3.1 BL1 3.2 BL2 4.小结 在之前汽车信息安全 -- 再谈车规MCU的安全启动文章里&#xff0c;我们详细描述了TC3xx 、RH850、NXPS32K3的安全启动流程&#xff0c;而在车控类ECU中&#xff0c;我们也基本按照这个流程…

vue+django+neo4j航班智能问答知识图谱可视化系统

&#x1f51e; 友友们&#xff0c;有需要找我&#xff0c;懂的都懂 &#x1fa75; 基于NLP技术知识图谱的航班知识智能问答 &#x1fa75; 技术架构&#xff1a;vue django mysql neo4j &#x1fa75; 数据&#xff1a;航班数据7万多条 &#x1fa75; vue知识图谱的模糊查询…

DICOM标准:核医学图像模块属性——核医学(Nuclear Medicine, NM)DICOM标准详解

目录 引言 1. NM 序列模块&#xff08;NM Sequence Module&#xff09; 1.1 NM序列模块属性 2. NM 设备模块&#xff08;NM Equipment Module&#xff09; 2.1 NM设备模块属性 3. NM 图像模块&#xff08;NM Image Module&#xff09; 3.1 NM图像模块属性 3.2 帧增量指针…

ViT面试知识点

文章目录 VITCLIPBlipSAMFast TransformerYOLO系列问题 BatchNorm是对一个batch-size样本内的每个特征做归一化&#xff0c;LayerNorm是对每个样本的所有特征做归一化。 Layer Normalization&#xff08;层归一化&#xff0c;简称LayerNorm&#xff09;是一种在深度学习中常用…

WPF使用Prism框架首页界面

1. 首先确保已经下载了NuGet包MaterialDesignThemes 2.我们通过包的项目URL可以跳转到Github上查看源码 3.找到首页所在的代码位置 4.将代码复制下来&#xff0c;删除掉自己不需要的东西&#xff0c;最终如下 <materialDesign:DialogHostDialogTheme"Inherit"Ide…

CTFshow之信息收集第1关到10关。详细讲解

得而不惜就该死&#xff01; --章总 开始新的篇章&#xff01; 零、目录 一、实验准备 1、ctf网站&#xff1a;ctf.show 2、工具&#xff1a;chrome浏览器、hackbar插件 3、burpsuite抓包工具 二、实验技巧 &#xff08;一&#xff09;F12摸奖 源码泄露 &#xff08;二…

企业CRM管理系统PHP源码/PHP客户关系CRM客户管理系统源码

系统功能实现 1、 公海管理:公海类型、客户公海。 2、 线索管理:我的线索、线索列表、线索状态、线索来源。 3、 客户管理:我的客户、客户列表、成交客户、行业类别、预查、地区列表、客户状态、客户级别。 4、 业绩订单:订单列表、我的订单。 5、 系统设置:系统设置…

40.第二阶段x86游戏实战2-初识lua

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

DAY17|二叉树Part03|LeetCode: 530.二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236.二叉树的最近公共祖先

目录 LeetCode: 530.二叉搜索树的最小绝对差 基本思路 有序数组法 C代码 双指针法 C代码 LeetCode: 501. 二叉搜索树中的众数 哈希法 C代码 双指针法 C代码 LeetCode: 236.二叉树的最近公共祖先 基本思路 C代码 LeetCode: 530.二叉搜索树的最小绝对差 力扣代码链…

「Mac畅玩鸿蒙与硬件27」UI互动应用篇4 - 猫与灯的互动应用

本篇将带领你实现一个趣味十足的互动应用&#xff0c;用户点击按钮时猫会在一排灯之间移动&#xff0c;猫所在的位置灯会亮起&#xff08;on&#xff09;&#xff0c;其余灯会熄灭&#xff08;off&#xff09;。应用会根据用户的操作动态更新灯光状态和文本提示当前亮灯的位置&…

【网络】套接字编程——UDP通信

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;UDP网络服务器简单模拟实现。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff1a;…