ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【MyBatis系列3】MyBatis SQL执行流程

【MyBatis系列3】MyBatis SQL执行流程 主要讲解MyBatis中SQL的执行流程基于MyBatis的基础知识进行更深层次的剖析。前言在《【MyBatis系列1】基础知识上》中我们讲解了MyBaits的工作原理以及它的四大核心组件的使用姿势包括SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和SQL Mapper。在《【MyBatis系列1】基础知识下》中通过完整的MayBatis使用示例详细讲解了MyBatis的XML配置文件。所以在阅读该文章前建议大家先看《【MyBatis系列1】基础知识》上-下两篇文章这篇文章主要是基于SQL的执行流程对MyBatis的基础知识进行更深层次的讲解涉及到之前已经讲过的基础知识会直接跳过。MyBatis 整体架构MyBatis 最上面是接口层接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义是查询、新增、更新还是删除操作中间层是数据处理层主要是配置 Mapper - XML 层级之间的参数映射SQL 解析SQL 执行结果映射的过程。上述两种流程都由基础支持层来提供功能支撑基础支持层包括连接管理事务管理配置加载缓存处理等。接口层在不与Spring 集成的情况下使用 MyBatis 执行数据库的操作主要如下InputStream is Resources.getResourceAsStream(myBatis-config.xml); SqlSessionFactoryBuilder builder new SqlSessionFactoryBuilder(); SqlSessionFactory factory builder.build(is); sqlSession factory.openSession();其中的SqlSessionFactory,SqlSession是 MyBatis 接口的核心类尤其是 SqlSession这个接口是MyBatis 中最重要的接口这个接口能够让你执行命令获取映射管理事务。这块知识的扩展可以参考《【MyBatis系列1】基础知识上》数据处理层配置解析在 Mybatis 初始化过程中会加载 mybatis-config.xml 配置文件、映射配置文件以及 Mapper 接口中的注解信息解析后的配置信息会形成相应的对象并保存到 Configration 对象中。之后根据该对象创建SqlSessionFactory 对象。待 Mybatis 初始化完成后可以通过 SqlSessionFactory 创建 SqlSession 对象并开始数据库操作。这块知识的扩展可以参考《【MyBatis系列1】基础知识上》SQL 解析与 scripting 模块Mybatis 实现的动态 SQL 语句几乎可以编写出所有满足需要的 SQL。Mybatis 中 scripting 模块会根据用户传入的参数解析映射文件中定义的动态 SQL 节点形成数据库能执行的SQL 语句。SQL 执行SQL 语句的执行涉及多个组件包括 MyBatis 的四大核心它们是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL 的执行过程可以用下面这幅图来表示MyBatis 层级结构各个组件的介绍SqlSession它是 MyBatis 核心 API主要用来执行命令获取映射管理事务。接收开发人员提供 Statement Id 和参数。并返回操作结果。Executor 执行器是 MyBatis 调度的核心负责 SQL 语句的生成以及查询缓存的维护。StatementHandler : 封装了JDBC Statement 操作负责对 JDBC Statement 的操作如设置参数、将Statement 结果集转换成 List 集合。ParameterHandler : 负责对用户传递的参数转换成 JDBC Statement 所需要的参数。ResultSetHandler : 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。TypeHandler : 用于 Java 类型和 JDBC 类型之间的转换。MappedStatement : 动态 SQL 的封装SqlSource : 表示从 XML 文件或注释读取的映射语句的内容它创建将从用户接收的输入参数传递给数据库的 SQL。Configuration: MyBatis 所有的配置信息都维持在 Configuration 对象之中。框架支持层反射模块Mybatis 中的反射模块对 Java 反射进行了很好的封装提供了简易的 API方便上层调用并且对反射操作进行了一系列的优化比如缓存了类的 元数据MetaClass和对象的元数据MetaObject提高了反射操作的性能。类型转换模块Mybatis 的别名机制能够简化配置文件该机制是类型转换模块的主要功能之一。类型转换模块的另一个功能是实现 JDBC 类型与 Java 类型的转换。在 SQL 语句绑定参数时会将数据由 Java 类型转换成 JDBC 类型在映射结果集时会将数据由 JDBC 类型转换成 Java 类型。日志模块在 Java 中有很多优秀的日志框架如 Log4j、Log4j2、slf4j 等。Mybatis 除了提供了详细的日志输出信息还能够集成多种日志框架其日志模块的主要功能就是集成第三方日志框架。资源加载模块该模块主要封装了类加载器确定了类加载器的使用顺序并提供了加载类文件和其它资源文件的功能。解析器模块该模块有两个主要功能一个是封装了 XPath为 Mybatis 初始化时解析 mybatis-config.xml配置文件以及映射配置文件提供支持另一个为处理动态 SQL 语句中的占位符提供支持。数据源模块Mybatis 自身提供了相应的数据源实现也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一很多开源的数据源都提供了丰富的功能如连接池、检测连接状态等选择性能优秀的数据源组件对于提供ORM 框架以及整个应用的性能都是非常重要的。事务管理模块一般地Mybatis 与 Spring 框架集成由 Spring 框架管理事务。但 Mybatis 自身对数据库事务进行了抽象提供了相应的事务接口和简单实现。缓存模块Mybatis 中有一级缓存和二级缓存这两级缓存都依赖于缓存模块中的实现。但是需要注意这两级缓存与Mybatis 以及整个应用是运行在同一个 JVM 中的共享同一块内存如果这两级缓存中的数据量较大则可能影响系统中其它功能所以需要缓存大量数据时优先考虑使用 Redis、Memcache 等缓存产品。Binding 模块在调用 SqlSession 相应方法执行数据库操作时需要制定映射文件中定义的 SQL 节点如果 SQL 中出现了拼写错误那就只能在运行时才能发现。为了能尽早发现这种错误Mybatis 通过 Binding 模块将用户自定义的Mapper 接口与映射文件关联起来系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作从而避免上述问题。注意在开发中我们只是创建了 Mapper 接口而并没有编写实现类这是因为 Mybatis 自动为 Mapper 接口创建了动态代理对象。MySQL执行流程这个也是摘录网上博客由于该包括主要讲源码我会剔除源码的部分只保留内容的讲解流程。SqlSessionFactorySqlSessionFactory 有两个实现类一个是 SqlSessionManager 类一个是 DefaultSqlSessionFactory 类DefaultSqlSessionFactory : SqlSessionFactory 的默认实现类是真正生产会话的工厂类这个类的实例的生命周期是全局的它只会在首次调用时生成一个实例单例模式就一直存在直到服务器关闭。SqlSessionManager 已被废弃原因大概是: SqlSessionManager 中需要维护一个自己的线程池而使用MyBatis 更多的是要与 Spring 进行集成并不会单独使用所以维护自己的 ThreadLocal 并没有什么意义所以 SqlSessionManager 已经不再使用。下面来对 SqlSessionFactory 的执行流程来做一个分析首先第一步是 SqlSessionFactory 的创建SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);从这行代码入手首先创建了一个 SqlSessionFactoryBuilder 工厂这是一个建造者模式的设计思想由 builder 建造者来创建 SqlSessionFactory 工厂然后调用 SqlSessionFactoryBuilder 中的 build 方法传递一个InputStream 输入流Inputstream 输入流中就是你传过来的配置文件 mybatis-config.xmlSqlSessionFactoryBuilder 根据传入的 InputStream 输入流和environment、properties属性创建一个XMLConfigBuilder对象。通过XML文件生成source - 通过source中的数据生成SqlSessionFactoryBuilder - 得到SqlSessionFactory - 拿到SqlSessionXML配置中的字段含义可以参考《【MyBatis系列1】基础知识下》。SqlSession通过 SqlSessionFactory 对象得到 SqlSession然后就可以执行 SQL 语句了。在 SqlSessionFactory.openSession 过程中它会调用到 DefaultSqlSessionFactory 中的 openSessionFromDataSource 方法这个方法主要创建了两个与我们分析执行流程重要的对象一个是 Executor 执行器对象一个是 SqlSession 对象。SqlSession 对象是 MyBatis 中最重要的一个对象这个接口能够让你执行命令获取映射管理事务。SqlSession 中定义了一系列模版方法让你能够执行简单的 CRUD 操作也可以通过 getMapper 获取 Mapper 层执行自定义 SQL 语句因为 SqlSession 在执行 SQL 语句之前是需要先开启一个会话涉及到事务操作所以还会有 commit、 rollback、close 等方法。这也是模版设计模式的一种应用。举个栗子SqlSession 控制数据库事务的方法如下所示//定义 SqlSession SqlSession sqlSession null; try { // 打开 SqlSession 会话 sqlSession SqlSessionFactory.openSession(); // some code... sqlSession.commit(); // 提交事务 } catch (IOException e) { sqlSession.rollback(); // 回滚事务 }finally{ // 在 finally 语句中确保资源被顺利关闭 if(sqlSession ! null){ sqlSession.close(); } }MapperProxyMapperProxy 是 Mapper 映射 SQL 语句的关键对象我们写的 Dao 层或者 Mapper 层都是通过 MapperProxy 来和对应的 SQL 语句进行绑定的。这就是 MyBatis 的核心绑定流程我们可以看到 SqlSession 首先调用 getMapper 方法。SqlSession 是大哥级别的人物只定义标准有一句话是怎么说的来着一流的企业做标准二流的企业做品牌三流的企业做产品SqlSession 不愿意做的事情交给 Configuration 这个手下去做但是 Configuration 也是有小弟的它不愿意做的事情直接甩给小弟去做这个小弟是谁呢它就是 MapperRegistry马上就到核心部分了。MapperRegistry 相当于项目经理项目经理只从大面上把握项目进度不需要知道手下的小弟是如何工作的把任务完成了就好。最终真正干活的还是 MapperProxyFactory。看到这段代码 Proxy.newProxyInstance 你是不是有一种恍然大悟的感觉如果你没有的话建议看一下这篇文章《【设计模式系列6】代理模式》。也就是说MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。MapperProxyFactory 会生成代理对象这个对象就是 MapperProxy最终会调用到 mapperMethod.execute 方法execute逻辑比较简单就是判断是 插入、更新、删除 还是 查询 语句。这里饶了一大圈说的简单一点其实就是通过sqlSession一层层拿到MapperProxyFactory然后拿到MapperProxy最后调用SQL操作接口时其实是通过MapperProxy中封装的execute()来执行的最后其实是走到了Executor中的逻辑。Executor每一个 SqlSession 都会拥有一个 Executor 对象这个对象负责增删改查的具体操作我们可以简单的将它理解为 JDBC 中 Statement 的封装版。也可以理解为 SQL 的执行引擎要干活总得有一个发起人吧可以把 Executor 理解为发起人的角色。执行器的创建类型是通过MyBatis XML文件配置的:settings !--取值范围 SIMPLE, REUSE, BATCH -- setting namedefaultExecutorType valueSIMPLE/ /settings不同的执行器介绍如下SimpleExecutor : 简单执行器是 MyBatis 中默认使用的执行器每执行一次 update 或 select就开启一个Statement 对象用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象)ReuseExecutor : 可重用执行器这里的重用指的是重复使用 Statement它会在内部使用一个 Map 把创建的Statement 都缓存起来每次执行 SQL 命令的时候都会去判断是否存在基于该 SQL 的 Statement 对象如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象所以我们缓存在 ReuseExecutor 上的 Statement作用域是同一个 SqlSession。BatchExecutor : 批处理执行器用于将多个 SQL 一次性输出到数据库CachingExecutor: 缓存执行器先从缓存中查询结果如果存在就返回之前的结果如果不存在再委托给Executor delegate 去数据库中取delegate 可以是上面任何一个执行器。Executor 的具体执行过程执行器所做的工作就完事了Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler通过MapperProxy找到操作接口后就开始通过Executor去执行具体的逻辑然后交给StatementHandler去执行。StatementHandlerStatementHandler 是四大组件中最重要的一个对象负责操作 Statement 对象与数据库进行交互在工作时还会使用 ParameterHandler 和 ResultSetHandler对参数进行映射对结果进行实体类的绑定。StatementHandler 的继承结构SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句。PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句。CallableStatementHandler管理 Statement 对象并调用数据库中的存储过程。MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建在创建完 PreparedStatement 之后我们需要对参数进行处理了参数处理通过ParameterHandler进行。ParameterHandlerParameterHandler 相比于其他的组件就简单很多了ParameterHandler 译为参数处理器负责为 PreparedStatement 的 sql 语句参数动态赋值它实现了这两个方法getParameterObject用于读取参数setParameters: 用于对 PreparedStatement 的参数赋值ResultSetHandlerMyBatis 只有一个默认的实现类就是 DefaultResultSetHandlerDefaultResultSetHandler 主要负责处理两件事处理 Statement 执行后产生的结果集生成结果列表处理存储过程执行后的输出参数在 DefaultResultSetHandler 中处理完结果映射并把上述结构返回给调用的客户端从而执行完成一条完整的SQL语句。MyBatis SQL执行全流程总结通过MyBatis XML配置文件生成source通过source中的数据生成并生成Executor和SqlSessionFactoryBuilder通过SqlSessionFactoryBuilder得到SqlSessionFactory通过SqlSessionFactory得到SqlSession通过SqlSession拿到MapperProxyFactory通过MapperProxyFactory拿到MapperProxy通过MapperProxy操作具体的SQL接口SQL接口的执行逻辑交给Executor执行(Executor之前就已经通过配置生成好了)Executor的执行逻辑交给StatementHandler执行StatementHandler会根据SQL类型选择SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler三者之一去执行SQL比如先执行PreparedStatementHandler通过PreparedStatementHandler处理数据前通过ParameterHandler给SQL语句动态赋值继续通过StatementHandler处理SQL逻辑SQL逻辑全部处理完后通过ResultSetHandler生成结果集返回数据给客户端
返回列表