真的,MyBatis 核心源码入门看这个就够了(四)

news/2024/4/25 20:49:52/文章来源:https://blog.csdn.net/yuiezt/article/details/129252432

4 SQL执行

再次回到demo的示例代码,

    @org.junit.jupiter.api.Testvoid queryBySn() throws IOException {//1 读取配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");//2 加载解析配置文件并获取SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//3 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//4 通过SqlSession中提供的API方法来操作数据库DeviceMapper deviceMapper = sqlSession.getMapper(DeviceMapper.class);Device device = deviceMapper.queryBySn(Device.builder().sn("2234567").build());System.out.println(device);//5 关闭会话sqlSession.close();}

看下SQL语句执行的入口:

Device device = deviceMapper.queryBySn(Device.builder().sn("2234567").build());

由于所有的Mapper都是JDK动态代理对象,所以任意方法都是执行触发管理类MapperProxy的invoke()

MapperProxy.invoke()
进入invoke方法,然后进入到 PlainMethodInvoker(内部类)的invoke方法:

public class MapperProxy<T> implements InvocationHandler, Serializable {......@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// toString hashCode equals getClass等方法,无需走到执行SQL的流程if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke// 普通方法会走到 PlainMethodInvoker(内部类) 的 invokereturn cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}......private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {super();this.mapperMethod = mapperMethod;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {// SQL执行的真正起点return mapperMethod.execute(sqlSession, args);}}......
}

MapperMethod.execute

  public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) { // 根据SQL语句的类型调用SqlSession对应的方法case INSERT: {// 通过 ParamNameResolver 处理args[] 数组 将用户传入的实参和指定参数名称关联起来Object param = method.convertArgsToSqlCommandParam(args);// sqlSession.insert(command.getName(), param) 调用SqlSession的insert方法// rowCountResult 方法会根据 method 字段中记录的方法的返回值类型对结果进行转换result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {// 返回值为空 且 ResultSet通过 ResultHandler处理的方法executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {// 返回值为 单一对象的方法Object param = method.convertArgsToSqlCommandParam(args);// 普通 select 语句的执行入口 >>result = 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;}

这一步根据不同的type(INSERT、UPDATE、DELETE、SELECT)和返回类型:
① 调用convertArgsToSqlCommandParam() 讲方法参数转换为SQL的参数。
② 调用sqlSession的insert()、update()、delete()、selectOne() 方法。
以查询为例,会走到selectOne方法。

    // 返回值为 单一对象的方法Object param = method.convertArgsToSqlCommandParam(args);// 普通 select 语句的执行入口 >>result = sqlSession.selectOne(command.getName(), param);

SqlSession.selectOne
对外接口的默认实现类:DefaultSqlSession

public class DefaultSqlSession implements SqlSession {......@Overridepublic <T> T selectOne(String statement, Object parameter) {List<T> list = this.selectList(statement, parameter);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;}}......@Overridepublic <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT);}......@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);// 如果 cacheEnabled = true(默认),Executor会被 CachingExecutor装饰return 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();}}......
}
  • selectOne() 最终也是调用了 selectList()。
  • 在selectList()中,我们先根据command name(Statement ID)从Configuration 中拿到MappedStatement。ms里面有xml中增删改查标签配置的所有属性,包括id、statementType、sqlSource、useCache、入参、出参等。
  • 然后执行了Executor的query()方法。
  • ExecutoropenSession的时候创建的,创建了执行器基本类型后,依次执行了二级缓存装饰、插件包装。如果有被插件包装,这里会先走到插件的逻辑。如果没有显式地在settings中配置 CacheEnable=false,再走到 CacheingExecutor 的逻辑,然后会走到BaseExecutor的query方法。

CachingExecutor.query

public class CachingExecutor implements Executor {@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 获取SQLBoundSql boundSql = ms.getBoundSql(parameterObject);// 创建CacheKey:什么样的SQL是同一条SQL? >>CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}
}

CacheKey 生成之后,调用另一个query()方法。

BaseExecutor.query

public abstract class BaseExecutor implements Executor {@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();// cache 对象是在哪里创建的?  XMLMapperBuilder类 xmlconfigurationElement()// 由 <cache> 标签决定if (cache != null) {// flushCache="true" 清空一级二级缓存 >>flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);// 获取二级缓存// 缓存通过 TransactionalCacheManager、TransactionalCache 管理@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// 写入二级缓存tcm.putObject(cache, key, list); // issue #578 and #116}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 {// 三种 Executor 的区别,看doUpdate// 默认Simplelist = 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;}
}

SimpleExecutor.doQuery

public class SimpleExecutor extends BaseExecutor {......@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandler >>StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}......
}

在Configuration.newStatementHandler()中,new 一个StatementHandler,先得到 RoutingStatementHandler。
RoutingStatementHandler里面没有任何的实现,是用来创建基本的StatementHandler的。这里会根据MappedStatement里面的statementType决定 StatementHandler 的类型。默认是 PREPARED(STATEMENT、PREPARED、CALLABLE)

/*** @author Clinton Begin* 实际上是用来选择具体的 StatementHandler 类型的* 持有了具体的 StatementHandler 的委托* 调用具体 StatementHandler 对象的方法*/
public class RoutingStatementHandler implements StatementHandler {......public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPAREDswitch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:// 创建 StatementHandler 的时候做了什么? >>delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}......
}

StatementHandler 里面包含了处理参数的 ParameterHandler和处理结果集的ResultSetHandler。这两个对象都是在上面new的时候创建的。

public abstract class BaseStatementHandler implements StatementHandler {......protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration();this.executor = executor;this.mappedStatement = mappedStatement;this.rowBounds = rowBounds;this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.objectFactory = configuration.getObjectFactory();if (boundSql == null) { // issue #435, get the key before calculating the statementgenerateKeys(parameterObject);boundSql = mappedStatement.getBoundSql(parameterObject);}this.boundSql = boundSql;// 创建了四大对象的其它两大对象 >>// 创建这两大对象的时候分别做了什么?this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);}......

这三个对象都是可以被插件拦截的四大对象之一,所以在创建之后都要用拦截器进行包装的方法。

public class Configuration {......public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);// 植入插件逻辑(返回代理对象)parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,ResultHandler resultHandler, BoundSql boundSql) {ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);// 植入插件逻辑(返回代理对象)resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);return resultSetHandler;}public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);// 植入插件逻辑(返回代理对象)statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}......
}

创建 Statement,用new出来的StatementHandler 创建 Statement对象。

public class SimpleExecutor extends BaseExecutor {......@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandler >>StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}......
}

执行查询操作,如果有插件包装,会先走到被拦截的业务逻辑。

// 执行查询
return handler.query(stmt, resultHandler);

进入到 PreparedStatementHandler中处理

public class PreparedStatementHandler extends BaseStatementHandler {......@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;// 到了JDBC的流程ps.execute();// 处理结果集return resultSetHandler.handleResultSets(ps);}......
}

执行 PreparedStatement 的 execute 方法,后面就是 JDBC 包中的PreparedStatement的执行了。
ResultSetHandler处理结果集,如果有插件包装,会先走到被拦截的业务逻辑。

SQL 执行时序图
SQL 执行时序图


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

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

相关文章

华为OD机试模拟题 用 C++ 实现 - 寻找连续区间(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明寻找连续区间题目输入输出示例一输入输出说明示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率…

华为OD机试题,用 Java 解【求解连续数列】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…

大数据算法自检

1 大数据亚线性空间算法 1.1 流模型的计数问题 问题定义&#xff1f;用什么算法&#xff1f;算法步骤&#xff1f;(提示&#xff1a;三层递进) 切比雪夫不等式&#xff1f;怎么证明&#xff1f;期望&#xff0c;方差&#xff0c;空间复杂度&#xff1f; 极其有限的空间存储极…

数据结构与算法(六):图结构

图是一种比线性表和树更复杂的数据结构&#xff0c;在图中&#xff0c;结点之间的关系是任意的&#xff0c;任意两个数据元素之间都可能相关。图是一种多对多的数据结构。 一、基本概念 图&#xff08;Graph&#xff09;是由顶点的有穷非空集合和顶点之间边的集合组成&#x…

每日分享(免登录积分商城系统 动力商城 兑换商城源码)

​demo软件园每日更新资源,请看到最后就能获取你想要的: 1.Python教程2022&#xff1a;100天从新手到大师 完整版 Python 100天从新手到大师是一个Python入门教程&#xff0c;Python从入门到精通&#xff0c;专门为热爱python的新手量身定做的学习计划&#xff0c;100天速成pyt…

OOM的俩种情况---主动kill/被动kill

出现OOM, 有两种处理方式&#xff1a;1. 主动Kill; 2. 被动Kill 例&#xff1a;HBase Region Server OOM定位问题复盘 现象 在HBase资源隔离项目中&#xff0c;对测试集群进行压测时&#xff0c;发现region server会出现崩溃的情况&#xff0c;单机请求量从>200到~50每秒都…

【Git使用教程】从入门到学废

文章目录1. 基础git流程图常用命令基本配置快捷指令解决GitBash乱码获取本地仓库基础操作指令查看修改的状态&#xff08;status&#xff09;添加工作区到暂存区(add)提交暂存区到本地仓库(commit)查看提交日志(log)版本回退添加文件至忽略列表总结2. 分支查看本地分支创建本地…

程序员多赚20k的接私活必备网站

为什么都是程序员&#xff0c;就有人能多赚20k&#xff1f;那是因为副业搞得那么溜啊&#xff01; 今天分享一些程序员搞钱必备的接私活网站&#xff0c;让更多程序员们在工作之余能有另外一份收入。 1.程序员客栈&#xff1a;http://proginn.com 专为程序员服务的软件外包对…

超级品牌符号怎么设计?大咖有方法

怎么设计超级LOGO图标&#xff1f;有方法&#xff01; LOGO设计大趋势&#xff1a;卡通化、拟人化 抽象符号已经泛滥 但卡通形象也已经泛滥 趣讲大白话&#xff1a;设计容易出名难 【安志强趣讲信息科技89期】 ******************************* 别以为设计一个卡通就牛X闪闪 比…

React Native使用echart——wrn-echarts

这里写自定义目录标题前言Tips详细使用过程如下1、开发环境搭建2、准备RN工程3、build App包4、 安装相关依赖5、试用Skia模式6、试用Svg模式7、封装Chart组件8、多个图表使用总结前言 平时写图表相关需求&#xff0c;用得最多的图表库就是echarts。echarts在web端的表现已经相…

机器学习:基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测

机器学习&#xff1a;基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测 作者&#xff1a;AOAIYI 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;AOAIYI首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞…

毕业设计 基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信

基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信1、项目简介1.1 系统构成1.2 系统功能2、部分电路设计2.1 STC89C52单片机核心系统电路设计2.2 dht11温湿度检测电路设计2.3 NRF24L01无线通信电路设计3、部分代码展示3.1 NRF24L01初始化3.2 NRF24L01的SPI写时序3.…

【数据库】数据库基本概念和类型

一、数据库基本概念 1、数据 所谓数据&#xff08;Data&#xff09;是指对客观事物进行描述并可以鉴别的符号&#xff0c;这些符号是可识别的、抽象的。它不仅仅指狭义上的数字&#xff0c;而是有多种表现形式&#xff1a;字母、文字、文本、图形、音频、视频等。现在…

STM32开发(16)----CubeMX配置DMA

CubeMX配置DMA前言一、什么是DMA&#xff1f;二、实验过程1.CubeMX配置2.代码实现3.实验结果总结前言 本章介绍使用STM32CubeMX对DMA进行配置的方法&#xff0c;DMA的原理、概念和特点&#xff0c;配置各个步骤的功能&#xff0c;并通过串口DMA传输实验方式验证。 一、什么是…

【学习笔记汇总】Github Note

本科毕业设计 Internet of Things environmental monitoring system based on STM32 STM32系列单片机工程模板 【STM32F103_Libary】基于STM32F103开发板的工程模板 ST7735屏幕【STM32F103Template】基于STM32F103开发板的工程模板 ILI9341屏幕【STM32F103_LibaryFinalVersio…

服务拆分及远程调用

目录 服务拆分 服务拆分注意事项 服务间调用 步骤一&#xff1a;注册RestTemplate 步骤二&#xff1a;修改业务层代码 总结&#xff1a; 提供者和消费者 思考 服务调用关系 服务拆分 服务拆分注意事项 单一职责&#xff1a;不同微服务&#xff0c;不要重复开发相同业…

电压放大器和电流放大器的区别是什么意思

在日常电子实验测试中&#xff0c;很多电子工程师都会使用到电压放大器和电流放大器&#xff0c;但是很多新手工程师却无法区分两者的区别&#xff0c;下面就让安泰电子来为我们讲解电压放大器和电流放大器的区别是什么意思。 一、电压放大器介绍&#xff1a; 电压放大器是一种…

2023王道考研数据结构笔记第一章绪论

第一章 绪论 1.1 数据结构的基本概念 1.数据&#xff1a;数据是信息的载体&#xff0c;是描述客观事物属性的数、字符以及所有能输入到计算机中并被程序识别和处理的符号的集合。 2.数据元素&#xff1a;数据元素是数据的基本单位&#xff0c;通常作为一个整体进行考虑和处理…

【MySQL】数据库中锁和事务的相关知识点

1.事务的四大特点 原子性&#xff1a;事务中的所有操作要么都成功&#xff0c;要么都失败。所有的操作是一个不可分割的单位。一致性&#xff1a;一致性指的是事务执行前后&#xff0c;数据从一个合法性状态转移到另一个合法性状态。这个状态和业务有关&#xff0c;是自己定义…

Editor工具开发实用篇:EditorGUI/EditorGUILayout的区别和EditorGUILayout的方法介绍

目录 一&#xff1a;EditorGUI和EditorGUILayout区别 二&#xff1a;EditorGUILayout 1.EditorGUILayout.BeginFadeGroup(float value); 2.EditorGUILayout.BeginHorizontal EditorGUILayout.BeginVertical 3.EditorGUILayout.BeginScrollView 4.EditorGUILayout.BeginT…