Mybatis原理解析之一 SqlSessionFactory生产(源码解析)

Mybatis原理解析之一 SqlSessionFactory生产(源码解析)缘起:笔者从业一年有余(此处撸撸胡须,摸摸自己半秃的头发)不经发出感叹:天天写ssm框架搭建下的业务框架,对于数据库操作也使用mybatis。但是扪心自问对于mybatis的使用只是停留在表层,从来没有研究过其原理,于是本着只有变秃才能变强的原则,于是准备研究一下mybatis的框架并再次做出记录。这里笔者对于mybatis的源码分析是在spring环境下进行的。这是笔者第一个研究的框架源…

大家好,又见面了,我是你们的朋友全栈君。

缘起:笔者从业一年有余(此处撸撸胡须,摸摸自己半秃的头发)不经发出感叹:天天写ssm框架搭建下的业务框架,对于数据库操作也使用mybatis。但是扪心自问对于mybatis的使用只是停留在表层,从来没有研究过其原理,于是本着只有变秃才能变强的原则,于是准备研究一下mybatis的框架并再次做出记录。这里笔者对于mybatis的源码分析是在spring环境下进行的。

  这是笔者第一个研究的框架源码,分析不准确的,请大家及时评论,互相交流,共同进步。

 再次立个flag,以后多分析源码并发表一个比之前有深度的it文章(最次两周写一篇博文)

目录

一、Spring与MyBatis整合框架

    1、目的

     2、spring下的mybatis配置

二、源码分析

      1、SqlSessionFactoryBean类

  2、SqlSessionFactoryBean的getObject()方法

    2.1、在spring环境获取SqlSeesionFactory对象

      2.2、afterPropertiesSet()方法

    2.3、buildSqlSessionFactory()方法


一、Spring与MyBatis整合框架

    1、目的

      本篇使用的框架环境是Spring 4.1.7 mybatis 3.3.0

     主要针对SqlsessionFactory对象在spring环境中的初始化过程,1、包括SqlSessionFactoryBean对象的配置

     2、mybatis.config.xml(mybatis配置文件的解析),3、mybatis配置中节点的注册到Configuration对象中。

     4、最终返回SqlSessionFactory对象

     2、spring下的mybatis配置

        spring环境下Mybatis的入口分析是:org.mybatis.spring.SqlSessionFactoryBean类,该类是生成Mybatis的SqlSessionFactory对象的工厂bean类。而SqlSessionFactory又是产生SqlSession对象的工厂类,SqlSession是我们进行CURD操作的核心对象。所以分析该类是Mybatis分析的入口同时也是核心。

	<!-- 3.配置SqlSessionFactory对象 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 注入数据库连接池 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
		<property name="configLocation" value="classpath:mybatis-config.xml" />
		<!-- 扫描entity包 使用别名 -->
		<property name="typeAliasesPackage" value="com.soecode.lyf.entity" />
		<!-- 扫描sql配置文件:mapper需要的xml文件 -->
		<property name="mapperLocations" value="classpath:mapper/*.xml" />
		<!--
            在mybatis.config中的相关设置都可以在spring环境初始化sqlSessionFactory对象中进行配置,
            初始化该对象时候,使用两种方式一种是mybatis-config配置文件,一种是在这里进行配置
			<property name="objectFactory" value=""/>
			<property name="plugins" value=""/>
		-->
	</bean>

    这里设置dataSource数据源,设置别名,扫描mapper.xml以及其他配置属性信息。  

  在该配置中有MyBatis的全局配置文件 —–mybatis-config.xml 这里面配置有很多丰富的配置便于我们使用自定义的节点来改变MyBatis的执行方式(比如TypeHandler,ObjectFactory),或者修改其缺省的配置信息等(<setting name=”cacheEnabled” value=”true” />开启/关闭全局缓存)。有关mybatis配置文件相关信息请参考 myBatis的配置文件详解。 如下附上一个mybatis-config.xml文件。

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 参数设置 -->
	<settings>
		<!-- 这个配置使全局的映射器启用或禁用缓存 -->
		<setting name="cacheEnabled" value="true" />
		<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->
		<setting name="lazyLoadingEnabled" value="true" />
		<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载 -->
		<setting name="aggressiveLazyLoading" value="true" />
		<!-- 允许或不允许多种结果集从一个单独的语句中返回(需要适合的驱动) -->
		<setting name="multipleResultSetsEnabled" value="true" />
		<!-- 使用列标签代替列名。不同的驱动在这方便表现不同。参考驱动文档或充分测试两种方法来决定所使用的驱动 -->
		<setting name="useColumnLabel" value="true" />
		<!-- 允许JDBC支持生成的键。需要适合的驱动。如果设置为true则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如Derby) -->
		<setting name="useGeneratedKeys" value="true" />
		<!-- 指定MyBatis如何自动映射列到字段/属性。PARTIAL只会自动映射简单,没有嵌套的结果。FULL会自动映射任意复杂的结果(嵌套的或其他情况) -->
		<setting name="autoMappingBehavior" value="PARTIAL" />
		<!--当检测出未知列(或未知属性)时,如何处理,默认情况下没有任何提示,这在测试的时候很不方便,不容易找到错误。 NONE : 不做任何处理 
			(默认值) WARNING : 警告日志形式的详细信息 FAILING : 映射失败,抛出异常和详细信息 -->
		<setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
		<!-- 配置默认的执行器。SIMPLE执行器没有什么特别之处。REUSE执行器重用预处理语句。BATCH执行器重用语句和批量更新 -->
		<setting name="defaultExecutorType" value="SIMPLE" />
		<!-- 设置超时时间,它决定驱动等待一个数据库响应的时间 -->
		<setting name="defaultStatementTimeout" value="25000" />
		<!--设置查询返回值数量,可以被查询数值覆盖 -->
		<setting name="defaultFetchSize" value="100" />
		<!-- 允许在嵌套语句中使用分页 -->
		<setting name="safeRowBoundsEnabled" value="false" />
		<!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 
			的类似映射。 -->
		<setting name="mapUnderscoreToCamelCase" value="false" />
		<!--MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 
			默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 
			的不同调用将不会共享数据。 -->
		<setting name="localCacheScope" value="SESSION" />
		<!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 
			NULL、VARCHAR OTHER。 -->
		<setting name="jdbcTypeForNull" value="OTHER" />
		<!-- 指定哪个对象的方法触发一次延迟加载。 -->
		<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
	</settings>
 
	<!-- 别名定义 -->
	<typeAliases>
		<typeAlias alias="pageAccessURL" type="com.lgm.mybatis.model.PageAccessURL" />
	</typeAliases>
 
	<!--自定义类型处理器 -->
	<typeHandlers>
		<!-- <typeHandler handler="com.xhm.util.BooleanTypeHandlder" /> -->
		<!--扫描整个包下的自定义类型处理器 -->
		<package name="com.xhm.util" />
	</typeHandlers>
 
	<!--plugins插件之 分页拦截器 -->
	<plugins>
		<plugin interceptor="com.xhm.util.PageInterceptor"></plugin>
	</plugins>
 
	<!--配置environment环境 -->
	<environments default="development">
		<!-- 环境配置1,每个SqlSessionFactory对应一个环境 -->
		<environment id="development1">
			<!-- 事务配置 type= JDBC、MANAGED 1.JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。 
				2.MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如Spring或JEE应用服务器的上下文)。 
				默认情况下它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,将closeConnection属性设置为false -->
			<transactionManager type="JDBC" />
			<!-- <transactionManager type="MANAGED"> <property name="closeConnection" 
				value="false"/> </transactionManager> -->
			<!-- 数据源类型:type = UNPOOLED、POOLED、JNDI 1.UNPOOLED:这个数据源的实现是每次被请求时简单打开和关闭连接。它有一点慢,这是对简单应用程序的一个很好的选择,因为它不需要及时的可用连接。 
				不同的数据库对这个的表现也是不一样的,所以对某些数据库来说配置数据源并不重要,这个配置也是闲置的 2.POOLED:这是JDBC连接对象的数据源连接池的实现,用来避免创建新的连接实例时必要的初始连接和认证时间。 
				这是一种当前Web应用程序用来快速响应请求很流行的方法。 3.JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用 -->
			<dataSource type="UNPOOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/xhm" />
				<property name="username" value="root" />
				<property name="password" value="root" />
				<!-- 默认连接事务隔离级别 <property name="defaultTransactionIsolationLevel" value="" 
					/> -->
			</dataSource>
		</environment>

 
	<!-- 映射文件,mapper的配置文件 -->
	<mappers>
		<!--直接映射到相应的mapper文件 -->
		<mapper resource="com/xhm/mapper/UserMapper.xml" />
		<!--扫描包路径下所有xxMapper.xml文件 -->
		<package name="com.xhm.mapper" />
	</mappers>
 
</configuration>

二、源码分析

      1、SqlSessionFactoryBean类

        public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>,
                                            InitializingBean, ApplicationListener<ApplicationEvent> {

       分析该类实现了三个接口

  • FactoryBean<SqlSessionFactory>接口         

       实现该接口是spring中的工厂bean 与spring中的普通bean不同,该工厂bean返回的是其生产出来的T对象,而非工厂本身 需要实现的方法getObject() 即返回SqlSessionFactory对象。(补充一点这个也是我们进行分析源码的入口)

    关于FactoryBean<T> 可以参考:https://www.cnblogs.com/quanyongan/p/4133724.html

  •  InitializingBean 接口

   在spring容器中管理的bean 在初始化的过程中会执行其接口中的afterPropertiesSet()方法

  详细解释参考: https://blog.csdn.net/z69183787/article/details/78415201

  • ApplicationListener<ApplicationEvent>接口

       用来监听spring容器的相关事件
      SqlSessionFactoryBean监听ContextRefreshedEvent 事件,该事件会在Spring容器初始化完成会触发该事件(暂时还不清楚)

  2、SqlSessionFactoryBean的getObject()方法

           补充知识:Configuration对象是构建SqlSessionFactory对象的包含的必要的配置信息,该配置对象的设置可以从两方面获取1.是Spring的配置中设置SqlSessionFactoryBean对象的properties可以设置,2.使用mybatis-config.xml进行是设置。

   在这两者配置方式下我们都可以配置如下的10个子节点

  1.      properties 

    属性都是可外部配置且可动态替换的,既可以在典型的Java属性文件中配置,亦可通过properties元素的子元素来传递。

    例如:

    <!– 
         mybatis的核心配置文件
             1.数据库的连接信息(连接池)
      –>
      <properties resource=”jdbc.properties”></properties>
    其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。
    比如:

      <!– 默认连接池 –>
      <dataSource type=”POOLED”>
           <property name=”driver” value=”${driverClass}”/>
           <property name=”url” value=”${url}”/>
           <property name=”username” value=”${userid}”/>
           <property name=”password” value=”${password}”/>
       </dataSource>

  2.      typeAliases  
      mybatis中设置bean的别名,方便我们在编写mapper文件时候的对于相关的bean名称的简化
  3.      plugins 
      mybatis的插件配置,插件对象其实是Mybatis框架中Interceptor接口的实现类,本质上来说是拦截器,例如我们添加的分页插件PageInterceptor,原理是在mybatis执行过程中动态拦截并增加其分页的功能。
    <plugins>
       <plugin interceptor="com.github.pagehelper.PageInterceptor">
          
       </plugin>
    </plugins>
  4.      objectFactory
         MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现
  5. objectWrapperFactory
        额。。。 这个对象呢?(今天天气不错哈)
  6. settings
       这些是极其重要的调整, 它们会修改 MyBatis 在运行时的行为方式
  7. environments
          Mybatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中,显示情况下有多重理由需要这么做。例如:开发,测试和生产环境需要不同的配置;或者共享相同的Schema的多个上产数据库
  8. databaseIdProvider
         MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 
  9. typeHandlers 
        无论是 MyBatis 在设置参数时,还是从结果集中取出值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。
  10. mappers 
      用来设置我们需要再加的mapper.xml文件(可以单独设置也可以用扫描包)。

    2.1、在spring环境获取SqlSeesionFactory对象

   调用该类的afterPropertiesSet()方法

 public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }

      2.2、afterPropertiesSet()方法

        构造SqlSessionFactory对象需要必须的依赖对象DataSource数据源对象(连接数据库的数据源对象 废话肯定必不可少)

 SqlSessionFactoryBuilder对象调用build()方法来构建SqlSessionFactory对象(也是必须的)。然后调用当前对象的buildSqlSessionFactory()方法。

    public void afterPropertiesSet() throws Exception {
       //判断数据源DataSource和SqlSeesionFactoryBuilder必须存在
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        //构造SqlSessionFactory对象
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

    2.3、buildSqlSessionFactory()方法

       各位这个是重点啊,是我们需要重点关注的东西,在这里进行详细的讲解所以将其分成几个部分来分别进行讲解(当然这里也是管中窥豹,只是将主要的流程进行大致的梳理,对其详细解释在后面的部分进行讲解)。

    1、创建XMLConfigBuilder对象

   针对解析我们的MyBatis-config.xml创建XMLConfigBuilder对象(用来解析该mybatis的配置文件),在后面进行对Configuration对象的设置使用(先解析配置)configLocation保存的是我们mybatis-config.xml信息(其实我们的相关配置都有缺省,当我们涉及修改的时候需要进行配置或者实现)
   逻辑:
           先判断有没有我们自定义的mybatis-config.xml,如果有创建该XMl解析对象xmlConfigBuilder(用来解析xml来生成Configuration对象,该对象是Mybatis创建SqlSesionFactory必须的配置)如果没有mybatis-config.xml该文件,则创建空的Configuration对象。 后面即进行相关的Configuration对象相关我们需要修改或者设置自定义实现的属性填充(比如 插件,seeting,properties,mapper,TypeHandler等对象)。

  Configuration configuration;
        /**
         * configLocation保存的是我们mybatis-config.xml信息(其实我们的相关配置都有缺省,当我们涉及修改的时候需要进行配置或者实现)
         * 逻辑:
         *   先判断有没有我们自定义的mybatis-config.xml,如果有创建该XMl解析对象xmlConfigBuilder(用来解析xml来生成Configuration对象,该对象是Mybatis创建SqlSesionFactory必须的配置)
         *   如果没有mybatis-config.xml该文件,则创建空的Configuration对象。
         *   后面即进行相关的Configuration对象相关我们需要修改或者设置自定义实现的属性填充(比如 插件,seeting,properties,mapper,TypeHandler等对象)
         */
        if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
            configuration = xmlConfigBuilder.getConfiguration();
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
            }

            configuration = new Configuration();
            configuration.setVariables(this.configurationProperties);
        }

           2、配置ObjectFactory/ObjectWrapperFactory

     objectFactory对象设置

  MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。默认情况下,我们不需要配置,mybatis会调用默认实现的objectFactory。 

   ObjectWrapperFactory对象设置

     暂时不清楚该节点对象的处理方式

/**
 * 设置ObjectFactory对象
 */
if (this.objectFactory != null) {          
     configuration.setObjectFactory(this.objectFactory);
}

 /**
 * 设置ObjectWrapperFactory对象
 */
 if (this.objectWrapperFactory != null) {
     configuration.setObjectWrapperFactory(this.objectWrapperFactory);
 }

   3、配置typeAliases/typeAliasesPackage

        前者是单独一个一个别名的设置,后者是针对该包下的所有别名进行设置注册,默认的别名规则是简单类名首字母小写。

     这里别名处理逻辑(包下的别名或者单个类的别名)
         *   1、从Configuration对象中获取typeAliasRegistry来进行别名的处理
         *   2、包下的获取所有包下的对于非接口,非匿名内部类的的全限定类名进行注册别名
         *   3、对于别名注册的类获取其简单类名。并获取类名上是否有@Alias注解value,如果有将其注解上的value值作为别名进行处理 ,传入该类class对象信息
         *   4、别名处理方式,首字母小写并作为key,该类的class对象作为value 存在在Map中
  补充
         *  其中Configuration对象进行相关信息配置的时候很多都是这种情况先从Configuration对象中获取XXXRegistry对象执行相应配置信息的注册
         *   如下
         *     1、mapper.xml信息配置
         *     protected MapperRegistry mapperRegistry;
         *     2、java数据类型和mysql类型映射处理器对象
         *     protected final TypeHandlerRegistry typeHandlerRegistry;
         *     3、别名配置信息
         *     protected final TypeAliasRegistry typeAliasRegistry;
         *     4、数据库驱动类型 mysql还是orcale,sqlserver
         *     protected final LanguageDriverRegistry languageRegistry;

//扫描包下的所有mapper对应的model进行别名的注册 
        String[] typeHandlersPackageArray;
        String[] arr$;
        int i$;
        String packageToScan;
  //获取包下所有类(为他们注册别名)
        if (StringUtils.hasLength(this.typeAliasesPackage)) {
            LanguageDriverRegistry
            typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n");
            arr$ = typeHandlersPackageArray;
            i$ = typeHandlersPackageArray.length;

            for(i$ = 0; i$ < i$; ++i$) {
                packageToScan = arr$[i$];
                //获取别名注册类(其实是使用map容器进行存储) 下面类似只是单独设置
                configuration.getTypeAliasRegistry().registerAliases(packageToScan, this.typeAliasesSuperType == null ? Object.class : this.typeAliasesSuperType);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
                }
            }
        }

//扫描单个的model进行别名的设置
        int len$;
        if (!ObjectUtils.isEmpty(this.typeAliases)) {
            Class[] arr$ = this.typeAliases;
            len$ = arr$.length;

            for(i$ = 0; i$ < len$; ++i$) {
                Class<?> typeAlias = arr$[i$];
                configuration.getTypeAliasRegistry().registerAlias(typeAlias);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type alias: '" + typeAlias + "'");
                }
            }
        }

4、设置插件plugins

       mybatis插件对应为拦截器会被放到拦截器链中进行执行,业务中常见的的插件即为分页插件等,所有的插件都是实现了Interceptor并放置在拦截器链中,在mybatis执行过程中依次执行。

  //设置插件相关配置
        //mybatis插件对应为拦截器会被放到拦截器链中进行执行,业务中常见的的插件即为分页插件
        if (!ObjectUtils.isEmpty(this.plugins)) {
            Interceptor[] arr$ = this.plugins;
            len$ = arr$.length;

            for(i$ = 0; i$ < len$; ++i$) {
                Interceptor plugin = arr$[i$];
                configuration.addInterceptor(plugin);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered plugin: '" + plugin + "'");
                }
            }
        }

   5、设置类型处理器typeHandlersPackage/typeHandlers

             所谓类型处理器是我们在mapper中设置传入参数/传出参数时候,数据库的数据类型(char varchar)和java的数据类型(String或者别的类型进行转换的转换处理器),以查询为例 传入参数Interger(id java类型),在执行过程中会被相应的类型处理器转换为mysql(以mysql为例)的int类型进行处理,在我们平常的工作很少自定义类型处理器使用,是因为mybatis给我们提供了很多自定义的TypeHandler,满足了我们的工作需求。设置mybatis的类型处理器和设置别名一样也有两种方式,一种是单独设置,另外一种是扫描包下所有实现TypeHandler接口的实现类。

  //设置包下所有类型处理器相关配置
        if (StringUtils.hasLength(this.typeHandlersPackage)) {
            typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n");
            arr$ = typeHandlersPackageArray;
            i$ = typeHandlersPackageArray.length;

            for(i$ = 0; i$ < i$; ++i$) {
                packageToScan = arr$[i$];
                configuration.getTypeHandlerRegistry().register(packageToScan);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
                }
            }
        }

        //设置类型处理器相关配置
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            TypeHandler[] arr$ = this.typeHandlers;
            len$ = arr$.length;

            for(i$ = 0; i$ < len$; ++i$) {
                TypeHandler<?> typeHandler = arr$[i$];
                configuration.getTypeHandlerRegistry().register(typeHandler);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type handler: '" + typeHandler + "'");
                }
            }
        }

  

   6、解析mybatis-config.xml

      mybatis提供了让我们使用xml配置文件的形式(笔者理解配置文件避免sqlsessionFactoryBean配置的臃肿,同时也便于我们进行修改,sqlsessionFactoryBean中的设置不是很全面),来设置相关的配置,里面完全包含mybatis的所有节点配置。

      //解析mybatis-config.xml,设置configuration(完善其配置信息)
        if (xmlConfigBuilder != null) {
            try {
                xmlConfigBuilder.parse();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
                }
            } catch (Exception var23) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var23);
            } finally {
                ErrorContext.instance().reset();
            }
        }

 从下面的解析文件可以看到,mybatis配置的所有10个子节点都可以在mybatis-config.xml中设置并被解析设置到Configuration对象中。

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
      settingsElement(root.evalNode("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);
    }
  }

 7、设置事务工厂

    mybatis与spring整合后则数据库的事务是有spring的事务管理器进行管理的,所以需要设置事务工厂

 8、设置环境environment

 environments元素节点可以配置多个environment子节点 

  假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment, 两个id分别对应开发环境(dev)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 例如,我将environments的deault属性的值配置为dev, 那么就会选择dev的environment。

 9、mapper.xml解析

    创建XmlMapperBuilder 进行mapper.xml解析(此处不详述 后面会重点分析)

//创建XmlMapperBuilder 进行mapper.xml解析
public void parse() {
     //判断对应啥的XXXMapper.xml是否被解析过,如果没有则进行解析,否则直接返回
    if (!configuration.isResourceLoaded(resource)) {
      //解析xml中的mapper根节点下的所有子节点(下面对其进行详细的分析)
      configurationElement(parser.evalNode("/mapper"));
      //保存资源的字符串名称
      configuration.addLoadedResource(resource);
      //将根据命名空间获取对应的XxxMapper.java
      //将命名空间namespace和mapper的类进行绑定,存放在Configuration对象中
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

   10、生成sqlSessionFactory对象  

 //SqlSessionFactoryBuidler使用Configuration对象来创建SqlSessionFactory并返回 
 this.sqlSessionFactoryBuilder.build(configuration);

 

至此SqlSessionFactory对象创建完成,并交由Spring容器管理,在我们进行数据库CURD操作时候,由该工厂创建SqlSession进行数据增删改查的操作。

 

     手打不易,看过觉得不错请点赞,谢谢

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/136767.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • python 使用 with open() as 读写文件

    python 使用 with open() as 读写文件读文件:要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符:&gt;&gt;&gt;f=open(‘E:\python\python\test.txt’,’r’)标示符’r’表示读,这样,我们就成功地打开了一个文件。如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在:…

  • k8s pod控制器_k8s控制器

    k8s pod控制器_k8s控制器k8sPod控制器的介绍ReplicaSet(RS)Deployment(Deploy)扩缩容镜像更新版本回退金丝雀发布Horizontal Pod Autoscaler(HPA)DaemonSet(DS)JobCronJob(CJ)StatefulSet(有状态)StatefulSet的金丝雀发布k8s的Pod控制器详解主要介绍各种Pod控制器的详细使用。Pod控制器的介绍在kubernetes中,按照Pod的创建方式可以将其分为两类:自主式Pod:kubernetes直接创建出来的Pod,这

  • python进阶(4)文件操作[通俗易懂]

    python进阶(4)文件操作[通俗易懂]文件操作文件操作主要包括对文件内容的读写操作,这些操作是通过文件对象实现的,通过文件对象可以读写文本文件和二进制文件open(file,mode='r',buffering=-

  • Android Fragment 使用

    自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragment谈上关系,做什么都要问下Fragment能实现不~~~哈哈,是不是有点过~~~本篇博客力求为大家说明Fragment如何产

    2021年12月22日
  • J2ME开发初探

    J2ME开发初探摘要:本文是J2ME开发的入门性文章,从零开始介绍了进行J2ME开发首先需要了解的一些东西。阅读本文几乎不需要相关的基础知识。1.1.       J2ME简介J2ME是Java2Platform,MicroEdition的简称。它是SunMicrosystems公司在Java的品脾之下的四种平台之一,其他三种分别是J2SE,J2EE和JavaCard。J2ME的目标是消费

  • Fisher Yates 洗牌算法「建议收藏」

    Fisher Yates 洗牌算法「建议收藏」//经典的洗牌算法,数组中随机抽一个元素与最后一个进行交换,下次在前n-1个元素中随机抽,依次类推直到最后一个voidshuffle(CREC*array,longn){longi,j;CRECtmp;for(i=n-1;i>0;i–){j=rand_long(i+1);tmp=a

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号