Mybatis执行流程简析

一、前言

日常工作中,我们用到mybatis的时候,都是写一个Mapper接口+xml文件/注解形式,然后就可以在业务层去调用我们在Mapper接口中定义的CRUD方法,很方便,但一直都没有去研究过执行逻辑,下面附一篇我自己研究的过程。

二、注入过程分析

平时我们在使用时,都是直接注解标在Mapper接口上,从而去注入一个实例,但我们是并没有实现过这个Mapper接口的,就很容易想到必然是有一个代理类(MapperProxy)来帮我们执行真正的过程,而且既然我们定义了接口,那大概率就是JDK动态代理了。

注入的过程也比较简单,在我们使用时会注明一个mapper扫描的包路径,然后在SqlSessionFactory初始化的过程中,会去解析每个mapper接口,并将其放在Configuration的MapperRegistry中,实际存放位置是在MapperRegistry中Map<Class<?>, MapperProxyFactory<?>> knownMappers。

接下来我们在使用mapper接口时,会从knownMappers中去获取到对应的MapperProxyFactory,从而去实例化真正的代理类,代码如下:

  public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {//关键步骤,里面会使用JDK动态代理,创建一个MapperProxyreturn mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}
public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();public MapperProxyFactory(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public Class<T> getMapperInterface() {return mapperInterface;}public Map<Method, MapperMethod> getMethodCache() {return methodCache;}@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}}

三、执行过程分析

综上来看,其实我们真正的方法是由MapperProxy来实现的,接下来看看它的逻辑,我们主要分析查询类的执行流程:

  /*** 执行入口*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}//缓存方法final MapperMethod mapperMethod = cachedMapperMethod(method);//真正的执行逻辑return mapperMethod.execute(sqlSession, args);}
/**
* 根据sql类型,执行不同的分支
* convertArgsToSqlCommandParam会转换传入的参数
*/
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);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://如果是void方法,并且自定义了ResultMap等映射,则执行此逻辑if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {//返参为集合类型result = 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);result = sqlSession.selectOne(command.getName(), param);}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;}

上面的查询方法,都会走到下面这两行代码

//获取sql执行的映射过程对象,包含了参数、数据源、返参等
MappedStatement ms = configuration.getMappedStatement(statement);
//执行查询方法
executor.query(ms, wrapCollection(parameter), rowBounds, handler);

接着会走到执行器executor,主要有两类实现:

      • org.apache.ibatis.executor.BaseExecutor(基本执行器)
      • CachingExecutor(带缓存的执行器)
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {//获取BoundSql对象,包含了解析动态SQl生成的sql语句以及参数映射的封装BoundSql boundSql = ms.getBoundSql(parameter);//生成缓存keyCacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//查询return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}
public BoundSql getBoundSql(Object parameterObject) {//调用sqlSource获取BoundSql,sqlSource默认为DynamicSqlSource类型(动态SQL)BoundSql boundSql = sqlSource.getBoundSql(parameterObject);List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//若参数映射为空,手动创建boundSqlif (parameterMappings == null || parameterMappings.isEmpty()) {boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);}// check for nested result maps in parameter mappings (issue #30)for (ParameterMapping pm : boundSql.getParameterMappings()) {String rmId = pm.getResultMapId();if (rmId != null) {ResultMap rm = configuration.getResultMap(rmId);if (rm != null) {hasNestedResultMaps |= rm.hasNestedResultMaps();}}}return boundSql;}
  public BoundSql getBoundSql(Object parameterObject) {//获取上下文对象,并将传入的入参对象及数据源标识放入bindingsDynamicContext context = new DynamicContext(configuration, parameterObject);//基于动态sql解析成的List<SqlNode> contents,遍历赋值参数rootSqlNode.apply(context);SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());BoundSql boundSql = sqlSource.getBoundSql(parameterObject);for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());}return boundSql;}

准备好BoundSql后,就该执行真正的查询了,主要链路如下

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

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

相关文章

使用simple_3dviz进行三维模型投影

【版权声明】 本文为博主原创文章&#xff0c;未经博主允许严禁转载&#xff0c;我们会定期进行侵权检索。 更多算法总结请关注我的博客&#xff1a;https://blog.csdn.net/suiyingy&#xff0c;或”乐乐感知学堂“公众号。 本文章来自于专栏《Python三维模型处理基础》的系列文…

飞鹅打印机使用注意事项:打印小票(云播报打印机)FP-V58-W(c)

文章目录 引言I 基础操作1.1 设置Wi-Fi1.2 在机器内预先内置logo 引言 应用场景&#xff1a; 云播报打印机&#xff1a;支持第三方软件开发商&#xff0c;接单后实现智能语音播报&#xff0c;可播报订单信息、打印订单小票。 http://www.feieyun.com/open/index.html 飞鹅对…

Android OpenGL ES 2.0入门实践

本文既然是入门实践&#xff0c;就先从简单的2D图形开始&#xff0c;首先&#xff0c;参考两篇官方文档搭建个框架&#xff0c;便于写OpenGL ES相关的代码&#xff1a;构建 OpenGL ES 环境、OpenGL ES 2.0 及更高版本中的投影和相机视图。 先上代码&#xff0c;代码效果如下图…

WPF自定义控件库之Window窗口

在WPF开发中&#xff0c;默认控件的样式常常无法满足实际的应用需求&#xff0c;我们通常都会采用引入第三方控件库的方式来美化UI&#xff0c;使得应用软件的设计风格更加统一。常用的WPF的UI控件库主要有以下几种&#xff0c;如&#xff1a;Modern UI for WPF&#xff0c;Mat…

Elasticsearch:使用 Elasticsearch 进行词汇和语义搜索

作者&#xff1a;PRISCILLA PARODI 在这篇博文中&#xff0c;你将探索使用 Elasticsearch 检索信息的各种方法&#xff0c;特别关注文本&#xff1a;词汇 (lexical) 和语义搜索 (semantic search)。 使用 Elasticsearch 进行词汇和语义搜索 搜索是根据你的搜索查询或组合查询…

0基础学习PyFlink——使用DataStream进行字数统计

大纲 sourceMapSplittingMapping ReduceKeyingReducing 完整代码结构参考资料 在《0基础学习PyFlink——模拟Hadoop流程》一文中&#xff0c;我们看到Hadoop在处理大数据时的MapReduce过程。 本节介绍的DataStream API&#xff0c;则使用了类似的结构。 source 为了方便&…

C# Onnx 用于边缘检测的轻量级密集卷积神经网络LDC

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace Onnx…

【HTML】HTML基础知识扫盲

1、什么是HTML&#xff1f; HTML是超文本标记语言&#xff08;Hyper Text Markup Language&#xff09;是用来描述网页的一种语言 注意&#xff1a; HTML不是编程语言&#xff0c;而是标记语言 HTML文件也可以直接称为网页&#xff0c;浏览器的作用就是读取HTML文件&#xff…

【网络协议】聊聊http协议

当我们输入www.baidu.com的时候&#xff0c;其实是先将baidu.com的域名进行DNS解析&#xff0c;转换成对应的ip地址&#xff0c;然后开始进行基于TCP构建三次握手的连接&#xff0c;目前使用的是1.1 默认是开启了keep-Alive。可以在多次请求中进行连接复用。 HTTP 请求的构建…

Bayes决策:身高与体重特征进行性别分类

代码与文件请从这里下载&#xff1a;Auorui/Pattern-recognition-programming: 模式识别编程 (github.com) 简述 分别依照身高、体重数据作为特征&#xff0c;在正态分布假设下利用最大似然法估计分布密度参数&#xff0c;建立最小错误率Bayes分类器&#xff0c;写出得到的决…

控梦术(一)之什么是清明梦

控梦术 首先&#xff0c;问大家一个问题。在梦中&#xff0c;你知道自己是在做梦吗&#xff1f;科学数据表明&#xff0c;大约23%的人在过去一个月中&#xff0c;至少有一次在梦中意识到自己正在做梦。科学家把这叫做清醒梦或者叫做清明梦。科学家说&#xff0c;每个人都能学会…

springboot的缓存和redis缓存,入门级别教程

一、springboot&#xff08;如果没有配置&#xff09;默认使用的是jvm缓存 1、Spring框架支持向应用程序透明地添加缓存。抽象的核心是将缓存应用于方法&#xff0c;从而根据缓存中可用的信息减少执行次数。缓存逻辑是透明地应用的&#xff0c;对调用者没有任何干扰。只要使用…

云计算与ai人工智能对高防cdn的发展

高防CDN&#xff08;Content Delivery Network&#xff09;作为网络安全领域的一项关键技术&#xff0c;致力于保护在线内容免受各种网络攻击&#xff0c;包括分布式拒绝服务攻击&#xff08;DDoS&#xff09;等。然而&#xff0c;随着人工智能&#xff08;AI&#xff09;和大数…

C#__委托delegate

委托存储的是函数的引用&#xff08;把某个函数赋值给一个委托类型的变量&#xff0c;这样的话这个变量就可以当成这个函数来进行使用了&#xff09; 委托类型跟整型类型、浮点型类型一样&#xff0c;也是一种类型&#xff0c;是一种存储函数引用的类型 using System.Reflec…

Linux网络基础2 -- 应用层相关

一、协议 引例&#xff1a;编写一个网络版的计算器 1.1 约定方案&#xff1a;“序列化” 和 “反序列化” 方案一&#xff1a;客户端发送形如“11”的字符串&#xff0c;再去解析其中的数字和计算字符&#xff0c;并且设限&#xff08;如数字和运算符之间没有空格; 运算符只…

RIS辅助MIMO广播信道容量

RIS辅助MIMO广播信道容量 摘要RIS辅助的BC容量矩阵形式的泰勒展开学习舒尔补 RIS-Aided Multiple-Input Multiple-Output Broadcast Channel Capacity论文阅读记录 基于泰勒展开求解了上行容量和最差用户的可达速率&#xff0c;学习其中的展开方法。 摘要 Scalable algorithm…

什么是神经网络,它的原理是啥?(1)

参考&#xff1a;https://www.youtube.com/watch?vmlk0rddP3L4&listPLuhqtP7jdD8CftMk831qdE8BlIteSaNzD 视频1&#xff1a; 简单介绍神经网络的基本概念&#xff0c;以及一个训练好的神经网络是怎么使用的 分类算法中&#xff0c;神经网络在训练过程中会学习输入的 pat…

Pmdarima实现单变量时序预测与交叉验证

目录 1. pmdarima实现单变量时间序列预测 2. 时间序列交叉验证 2.1 滚动交叉验证(RollingForecastCV) 2.2 滑窗交叉验证(SildingWindowForecastCV) 1. pmdarima实现单变量时间序列预测 Pmdarima是以statsmodel和autoarima为基础、封装研发出的Python时序分析库、也是现在市…

故障诊断模型 | Maltab实现GRU门控循环单元故障诊断

文章目录 效果一览文章概述模型描述源码设计参考资料效果一览 文章概述 故障诊断模型 | Maltab实现GRU门控循环单元故障诊断 模型描述 利用各种检查和测试方法,发现系统和设备是否存在故障的过程是故障检测;而进一步确定故障所在大致部位的过程是故障定位。故障检测和故障定位…