深度解析MyBatis和运行原理

本文详细解析了MyBatis的运行过程,包括SqlSessionFactory的构建、Configuration对象的初始化及映射器内部组件的构成等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MyBatis的运行过程分为两大步:第1步,读取配置文件缓存到Configuration对象,用以创建SqlSessionFactory;第2步,SqlSession的执行过程  。


1.构建SqlsessionFactory过程

SqlsessionFactory是Mybatis的核心类之一,其最重要的功能就是提供创建Mybatis的核心接口SqlSession,所以要先创建SqlsessionFactory,为此要提供配置文件和相关参数.MyBatis是一个复杂的系统,它采用了Builder(建造者)模式去创建SqlsessionFactory,在实际中可以通过SqlSessionBuilder去构建,其构建分为两步。

第1步:通过org.apache.ibatis.builder.xml.XMLConfigBuilder解析配置的XML文件,读出所配置的参数,并将读取的内容存入org.apache.ibatis.session.Configuration类对象中。而Configuration采用的单例模式,几乎所有的MyBatis配置内容都会存放在这个单例对象中,以便后续将这些内容读出.

(1.将xml的配置信息解析存储到Configuration对象中)

第2步:使用Configuration对象去创建SqlSessionFactory。MyBatis中的SqlSessionFactory是一个接口,而不是一个实现类,为此MyBatis提供了一个默认实现类org.apache.ibatis.session.defaults.DefaultSqlSessionFactory。在大部分情况下都没有必要自己去创建新的SqlSessionFactory实现类

    (2.使用Configuration创建SqlSessionFactory->SqlSessionFactory是接口,MyBatis有默认SqlSessionFactory实现类实现)

这种创建方式就是一种Builder模式-->对于复杂的对象而言,使用构造桉树很难实现。这时使用一个类(比如Configuration)作为统领,一步步构建所需的内容,然后通过它去创建最终的对象(比如SqlSessionFactory),这样每一步都会很清晰,这种方式值得学习,并且在工作中使用。

MyBatis是如何注册typeHandler的?    ↓

XMLConfigBuilder部分源码:

package org.apache.ibatis.builder.xml;

/**imports**/
public class XMLConfigBuilder extends BaseBuilder {
......
 private void parseConfiguration(XNode root) {
    try {
      Properties settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
......
}




从源码中可以看到,它是通过一步步解析XML的内容得到对应的信息的,而这些信息正式我们在配置文件中的配置内容。对于实际工作而言,也许并不需要全部元素都讨论源码  这里对typeHandlers解析的方法进行深一步讨论,以揭示MyBatis的解析过程.

    配置的typeHandler都会被注册到typeHandlerRegisety(类型的处理程序注册表)对象中去,继续查看源码可以知道它的定义是放在XMLConfigBuilder的父类BaseBuilder中的,为此研究一下BaseBuilder类的源码

package org.apache.ibatis.builder;
public abstract class BaseBuilder {

 protected final TypeHandlerRegistry typeHandlerRegistry;
 protected final TypeHandlerRegistry typeHandlerRegistry;

 public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

...
}

从源码可以知道,typeHandlerRegistry对象其实就是Configuration单例的一个属性,所以通过Configuration单例拿到typeHandlerRegistry对象,进而拿到我们所注册的typeHandler。

    这就是MyBatis注册typeHandler的方式,它也是用类似的方式注册其他的配置内容的。

(XMLConfigBuilder解析XML配置内容,MyBatis将其注册到对应的HandlerRegistry对象上,进而拿到注册的Handler)




1.1.构建Configuration

在SqlSessionFactory构建中,Configuration是最重要的,它的作用是:

  • 读取配置文件,包括基础配置中的XML和映射器XML(或注释)。
  • 初始化一些基础配置,比如MyBatis的别名等,一些重要的对象(比如插件,映射器,Object工厂,typeHandler对象等)。
  • 提供单例,为后续创建SessionFactory服务,提供配置的参数.
  • 执行一些重要的对象的初始化方法。


    显然Configuration不会是一个简单的类,MyBatis的配置信息都来自于此。如果分析源码的话。我们会发现,几乎所有配置都可以在这里找到踪影。Configuration是通过XMLConfigBuilder去构建的,首先它会读出所有XML配置的信息,然后把他们解析并保存在Configuration单例中。它会做如下初始化:
  • properties 全局参数。
  • typeAliases 别名。
  • Plugins 插件。
  • objectFactory 对象工厂。
  • ObjectWrapperFactory 对象包装工厂。
  • reflectionFactory 反射工厂
  • settings 环境配置
  • environments  数据库环境
  • databaseIdProvider 数据库标识
  • typeHandlers  类型转换器
  • Mappers 映射器

    它们都会以类似typeHandler注册那样的方法被存放到Configuration单例中,以便未来将其取出。
    1.2.构建映射器的内部组成
    插件需要频繁访问映射器的内部组成。
    ~当XMLConfiguration解析XML时,会将每一个SQL和其配置的内容保存起来,那么是如何保存的呢?
    一般而言,在MyBatis中有一条SQL和它相关的配置信息时由三个部分组成的,它们分别是MappedStatementSqlSourceBoundSql
  • MappedStatement的作用 是暴君一个映射器节点(select|insert|delete|update)的内容。它是一个类,包括许多我们配置的SQL、SQL的id、缓存信息、parameterMap、parameterType,resultType,resultMap、languageDriver等重要配置内容。它还有一个重要的属性sqlSourceMyBatis通过它来获取某条SQL配置的所有信息。
  • SqlSource是提供BoundSql对象的地方,它是MappedStatement的一个属性。注意,它是一个接口,而不是一个实现类。对它而言有那么重要的几个实现类:DynamicSqlSource、ProviderSqlSource、RawSQLSource、StaticSource。它的作用根据上下文和参数解析生成需要的SQL,很多时候我们采取DynamicSqlSource配合参数进行解析后得到SQL,减少SQL冗余。这个接口只定义了一个接口方法--getBoundSql(parameterObject),使用它就可以得到一个BoundSql对象。
  • BoundSql是一个结果对象,也就是SqlSource通过对SQL和参数的联合解析得到SQL和参数,它是建立SQL和参数的地方,它有3个常用属性:sql,parameterObject,parameterMappings,后面讨论。

    在插件的应用常常会使用它们。当然解析的过程还是比较复杂的,但是在大部分的情况下,请不需要去理会解析和组装SQL规则,因为大部分的插件只要做很小的动作的变化就可以了,而无须对它们进行大幅度的修改,因为大幅度修改会导致大量的底层被重写,所以我们主要关注BoundSql对象,通过它便可以拿到执行的SQL和参数,通过SQL和参数就可以来增强MyBatis底层的功能。

    现在来看看映射器的内部组成:

    注意,这里只列举了主要的方法和属性.MappedStatement对象涉及的东西比较多,一般不去修改它,因为容易产生不必要的错误。SqlSource是一个接口,它的主要作用是根据参数和其他规则组装SQL,这些都是很复杂的东西,好在MyBatis本身已经实现了它们,一般不去修改。对于最终的参数和SQL都反应在BoundSql类对象上,在插件中往往需要拿到它进而可以拿到当前运行的SQL和参数,从而对运行过程做出必要的修改,来满足特殊的需求这便是MyBatis插件提供的功能,所以这里的论述是对MyBatis插件开发是至关重要的.
    由图可知 BoundSql会提供3个主要的属性:parameterMappings,parameterObject和sql。
    ①parameterObject为参数本身,可以传递简单对象、POJO或者Map、@Param注解的参数,由于它在插件中相当有用 ,我们有必要讨论它的一些规则.
  • 传递简单对象,包括int,String,float,double,当传递int类型时,MyBatis会把参数变为Integer对象传递,类似的long,String,float,double也是如此。
  • 传递POJO或者Map,parameterObejct就是传入POJO或者Map.
  • 传递多个参数,如果没有@Param注解,那么MyBatis会把parameterObject变为一个Map<String,Obejct>对象,其键值的关系是按顺序来规划的,类似于{"1",p1,"2":"p2","3","p3"......,"param1":p1,"param2":p2,"param3":p3........}这样的形式,所以在编写时可以使用#{param1}或者或者#{1}去引用第一个参数。
  • 使用@Param注解,MyBatis就会把parameterObject也变成一个Map<String,Object>对象,类似于没有@Param注解,只是把起数字的兼职置换成@Param注解键值。比如注解@Param("key1") String p1、@Param("key2") int p2、@Param("key3") Role p3 ,那么parameterObject对象就是一个Map<String,Objet>,它的键值包含{"key1":p1,"key2":p2,"key3":p3,"param1":p1,"param2":p2,"param3":p3}。
  • parameterMappings是一个List,它的每一个元素都是ParameterMapping对象,对象会描述参数,参数包括属性名称、表达式、javaType,jdbcType,typeHandler等重要信息,一般不需要去改变它,通过它就可以实现参数和SQL的结合,以便PrparedStatement能够通过它找到parmeterObject对象的属性设置参数,使程序准确运行。
  • sql属性就是书写在映射器里面的一条被SqlSource解析后的SQL。在大部分时候无须修改它,只是在使用插件时可以根据需要进行改写,改写SQL将是一件危险的事情,需要考虑周全。
    1.3  构建SqlSessionFactory
  • 有了COnfiguration对象构建木构件SqlSessionFactory是很简单的。
    new SqlSessionFactoryBuilder().build(reader);
    通过上分析我们知道MyBatis会根据文件流先生成Configuration对象,进而构建SQLSessionFactory对象,真正的难点是构建Configuration对象。
  • 到这里:
    RUN->XMLConfigBuidler解析配置的XML文件-->存放Configuration单例中->解析时注册HandlerRegistry,HandlerRegistry为Configuration单例的一个属性,可以进而拿到typeHandler -->进而Configuration单例完成初始化-->new SqlSessionFactoryBuilder().build(文件流)-->得到SqlSessionFactory对象。

......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值