上一篇介绍了反射和动态代理基础,主要是为本篇文章做个铺垫,反射使配置和灵活性大大提高,可以给很多配置设置参数,动态代理可以在运行时创建代理对象,做一些特殊的处理。

本篇会介绍MyBatis解析和运行原理,下一篇介绍插件及应用,目的是更好地编写插件,通过本篇的介绍,你会了解到:

  • 构建SqlSessionFactory过程
  • 映射器的动态代理
  • SqlSession的4大对象
  • sql执行的过程

SqlSessionFactory和SqlSession是MyBatis的核心组件,在文章 JDBC和MyBatis介绍 中有详细说明。

构建SqlSessionFactory过程

构建主要分为2步:

  • 通过XMLConfigBuilder解析配置的XML文件,读出配置参数,包括基础配置XML文件和映射器XML文件;
  • 使用Configuration对象创建SqlSessionFactory,SqlSessionFactory是一个接口,提供了一个默认的实现类DefaultSqlSessionFactory。

说白了,就是将我们的所有配置解析为Configuration对象,在整个生命周期内,可以通过该对象获取需要的配置。

由于插件需要频繁访问映射器的内部组成,会重点这部分,了解这块配置抽象出来的对象:

MappedStatement

它保存映射器的一个节点(select|insert|delete|update),包括配置的SQL,SQL的id、缓存信息、resultMap、parameterType、resultType等重要配置内容。

它涉及的对象比较多,一般不去修改它。

SqlSource

它是MappedStatement的一个属性,主要作用是根据参数和其他规则组装SQL,也是很复杂的,一般也不用修改它。

BoundSql

对于参数和SQL,主要反映在BoundSql类对象上,在插件中,通过它获取到当前运行的SQL和参数以及参数规则,作出适当的修改,满足特殊的要求。

BoundSql提供3个主要的属性:parameterObject、parameterMappings和sql,下面分别来介绍。

parameterObject为参数本身,可以传递简单对象、POJO、Map或@Param注解的参数:

  • 传递简单对象(int、float、String等),会把参数转换为对应的类,比如int会转换为Integer;
  • 如果传递的是POJO或Map,paramterObject就是传入的POJO或Map不变;
  • 如果传递多个参数,没有@Param注解,parameterObject就是一个Map<String,Object>对象,类似这样的形式{"1":p1 , "2":p2 , "3":p3 ... "param1":p1 , "param2":p2 , "param3",p3 ...},所以在编写的时候可以使用#{param1}或#{1}去引用第一个参数;
  • 如果传递多个参数,有@Param注解,与没有注解的类似,只是将序号的key替换为@Param指定的name;

parameterMappings,它是一个List,元素是ParameterMapping对象,这个对象会描绘sql中的参数引用,包括名称、表达式、javaType、jdbcType、typeHandler等信息。

sql,是写在映射器里面的一条sql。

有了Configuration对象,构建SqlSessionFactory就简单了:

sqlSessionFactory = new SqlSessionFactoryBuilder().bulid(inputStream);

SqlSession运行过程

映射器的动态代理

Mapper映射是通过动态代理来实现的,使用JDK动态代理返回一个代理对象,供调用者访问。

首先看看实现InvocationHandler接口的类,它是执行本代理方法的关键,可以看到,Mapper是一个接口,会生成MapperMethod对象,调用execute方法。

public class MapperProxy<T> implements InvocationHandler, Serializable {.....@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);}
}

看下面的代码,MapperMethod采用命令模式,根据不同的sql操作,做不同的处理。

public class MapperMethod {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;......}}}

最后看下,生成代理类的方法,就是使用JDK动态代理Proxy来创建的。

public class MapperProxyFactory<T> {public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}}

总结下映射器的调用过程,返回的Mapper对象是代理对象,当调用它的某个方法时,其实是调用MapperProxy#invoke方法,而映射器的XML文件的命名空间对应的就是这个接口的全路径,会根据全路径和方法名,便能够绑定起来,定位到sql,最后会使用SqlSession接口的方法使它能够执行查询。

SqlSession下的四大对象

通过上面的分析,映射器就是一个动态代理对象,进入到了MapperMethod的execute方法,它经过简单的判断就进入了SqlSession的删除、更新、插入、选择等方法,这些方法如何执行是下面要介绍的内容。

Mapper执行的过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库操作和结果返回的,理解他们是编写插件的关键:

  • Executor:执行器,由它统一调度其他三个对象来执行对应的SQL;
  • StatementHandler:使用数据库的Statement执行操作;
  • ParameterHandler:用于SQL对参数的处理;
  • ResultHandler:进行最后数据集的封装返回处理;

在MyBatis中存在三种执行器:

  • SIMPLE:简易执行器,默认的执行器;
  • REUSE:执行重用预处理语句;
  • BATCH:执行重用语句和批量更新,针对批量专用的执行器;

以SimpleExecutor为例,说明执行过程

public class SimpleExecutor extends BaseExecutor {/*** 执行查询操作*/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(this, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}/*** 初始化StatementHandler*/private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);stmt = handler.prepare(connection);handler.parameterize(stmt);return stmt;}/*** 执行查询*/@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleResultSets(statement);}
}

可以看到最后会委托给StatementHandler会话器进行处理,它是一个接口,实际创建的是RoutingStatementHandler对象,但它不是真实的服务对象,它是通过适配器模式找到对应的StatementHandler执行的。在MyBatis中,StatementHandler和Executor一样分为三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。

Executor会先调用StatementHandler的prepare方法预编译SQL语句,同时设置一些基本运行的参数。然后调用parameterize()方法启用ParameterHandler设置参数,完成预编译,跟着执行查询,用ResultHandler封装结果返回给调用者。

参数处理器和结果处理器比较简单,就不在此介绍了。

下一篇会介绍插件及其应用,主要是在sql执行的过程中,在四大对象的基础上进行扩展。

欢迎扫描下方二维码,关注我的个人微信公众号 ~

转载于:https://blog.51cto.com/13714880/2112520

深入浅出MyBatis:MyBatis解析和运行原理相关推荐

  1. mybatis传递多个参数_深入浅出MyBatis:MyBatis解析和运行原理

    原文:https://juejin.im/post/5abcbd946fb9a028d1412efc 本篇文章是「深入浅出MyBatis:技术原理与实践」书籍的总结笔记. 上一篇介绍了反射和动态代理基 ...

  2. python socket读取数据不能解析_通过实例解析return运行原理,除了quot;生孩子quot;python真是无所不能啊...

    文章内容主要介绍了通过实例解析Python return运行原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下!!! return 语句就是讲结果返 ...

  3. 试解析Tomcat运行原理(一)--- socket通讯(转)

    关于这篇文章也确实筹划了很久,今天决定开篇写第一篇,说起tomcat首先很容易联想到IIS,因为我最开始使用的就是.net技术,我第一次使用asp写学生成绩管理系统后,很茫然如何让别人都能看到或者说使 ...

  4. 试解析Tomcat运行原理(一)--- socket通讯

    关于这篇文章也确实筹划了很久,今天决定开篇写第一篇,说起tomcat首先很容易联想到IIS,因为我最开始使用的就是.net技术,我第一次使用asp写学生成绩管理系统后,很茫然如何让别人都能看到或者说使 ...

  5. Mybatis运行原理及源码解析

    Mybatis源码解析 一.前言 本文旨在mybatis源码解析,将整个mybatis运行原理讲解清楚,本文代码地址: https://github.com/lchpersonal/mybatis-l ...

  6. MyBatis框架 基本配置及运行原理

    MyBatis(半自动,轻量级)简介 原名iBatis,2013年迁移到gitHub,sql与Java编码分离,sql是开发人员控制 PS:对于JDBC,sql夹杂在java代码块中,耦合度高导致编码 ...

  7. MyBatis 实现多表查询、resultMap 标签、MyBatis 注解、mybatis运行原理

    内容 Auto Mapping 单表实现(别名方式) 实现单表配置 单个对象关联查询(N+1,外连接) 集合对象关联查询 注解开发 MyBatis 运行原理 一.MyBatis 实现多表查询 Myba ...

  8. mybatis运行原理(面试回答)

    在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.下面 需要实例化 SqlSessionFactoryBuilder 构建器.帮助 SqlSessionFactory 接 ...

  9. MyBatis运行原理(二)SqlSession对象创建过程分析

    PS:这篇博文承接上一篇: MyBatis运行原理(一)SqlSessionFactory对象创建过程分析 在上一篇博文中分析了SqlSessionFactory对象创建的过程,有了SqlSessio ...

  10. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

最新文章

  1. 【Qt】调用Python函数:无参数、单个参数、多个参数、数组参数
  2. MATLAB对比度调节工具
  3. Struts2学习8--文件上传(多个文件上传)
  4. ajax传递json数组php,怎么通过ajax传送json数组到php,并通过php将数据插入数据库
  5. P2662 牛场围栏(同余最短路)
  6. java log4j权限被否定_SLF4J简介与使用(整合log4j)
  7. 【Codeforces】925A Stairs and Elevators【贪心】
  8. 利用输入输出流及文件类编写一个程序,可以实现在屏幕显示文本文件的功能,类似DOS命令中的type命令
  9. RocketMQ消费端消息回退(消费重试)机制源码解析
  10. 科普:史上最强单片机
  11. 51单片机基本刷屏测试实验_320x240真彩TFT屏51单片机驱动刷屏程序
  12. 微量样本RNA甲基化m6A技术比较
  13. OMNeT++下载、安装及实例tictoc1-tictoc18
  14. 你一定要收藏的全网最完整CAD快捷键大全!
  15. Python初学笔记4-【嵌套循环】
  16. Qt开发QtQuick程序在编译时报错:0x00007FFE4E60BB6B (ig9icd64.dll) 处有未经处理的异常: 0xC0000005: 读取位置 0xFFFFFFFFFFFFF
  17. GO项目性能优化大赏
  18. 出版java类的书籍需要多少钱,出版一本书需要花费多少资金?
  19. CSUSTOJ-藤原书记想要探病(简单矩阵快速幂)
  20. 想成为我的同事,不会点Linux怎么行!

热门文章

  1. java集群如何同步_Kafka 跨集群同步方案
  2. 已处理证书链,但是在不受信任提供程序信任的根证书中终止 - Windows 7安装.Net Framework 4.7.2时出现此问题
  3. ThinkPhp报错:thinkphp\library\think\Template.php Line(1243) template not exists:...test\...\index.html
  4. Jquery获取表格tr对象,并循环获取表格内容
  5. linux远程升级运行程序,在LINUX上对DSP程序远程升级的实现想法
  6. 修改JDK的经历:两处字体的粗体代码引起的错误
  7. 不同CPU指令的指令集密度
  8. LINUX编译ARM64/AARCH64版本的jogamp(gluegen/jogl)注意事项
  9. 错误及原因推测:sysdeps/x86_64/multiarch/strstr-sse2-unaligned.S: 没有那个文件或目录
  10. 物质为何能在虚空粒子海中存在