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

2022-09-05 14:59:52 浏览数 (1)

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

缘起:笔者从业一年有余(此处撸撸胡须,摸摸自己半秃的头发)不经发出感叹:天天写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分析的入口同时也是核心。

代码语言:javascript复制
	<!-- 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文件。

代码语言:javascript复制
<?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个子节点

properties

typeAliases mybatis中设置bean的别名,方便我们在编写mapper文件时候的对于相关的bean名称的简化

plugins mybatis的插件配置,插件对象其实是Mybatis框架中Interceptor接口的实现类,本质上来说是拦截器,例如我们添加的分页插件PageInterceptor,原理是在mybatis执行过程中动态拦截并增加其分页的功能。

objectFactory MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现

objectWrapperFactory 额。。。 这个对象呢?(今天天气不错哈)

settings 这些是极其重要的调整, 它们会修改 MyBatis 在运行时的行为方式

environments Mybatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中,显示情况下有多重理由需要这么做。例如:开发,测试和生产环境需要不同的配置;或者共享相同的Schema的多个上产数据库

databaseIdProvider MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。

typeHandlers 无论是 MyBatis 在设置参数时,还是从结果集中取出值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。

mappers 用来设置我们需要再加的mapper.xml文件(可以单独设置也可以用扫描包)。

2.1、在spring环境获取SqlSeesionFactory对象

调用该类的afterPropertiesSet()方法

代码语言:javascript复制
 public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }

2.2、afterPropertiesSet()方法

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

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

代码语言:javascript复制
    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等对象)。

代码语言:javascript复制
  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对象设置

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

代码语言:javascript复制
/**
 * 设置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;

代码语言:javascript复制
//扫描包下的所有mapper对应的model进行别名的注册 
        String[] typeHandlersPackageArray;
        String[] arr$;
        int i$;
        String packageToScan;
  //获取包下所有类(为他们注册别名)
        if (StringUtils.hasLength(this.typeAliasesPackage)) {
            LanguageDriverRegistry
            typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; tn");
            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执行过程中依次执行。

代码语言:javascript复制
  //设置插件相关配置
        //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接口的实现类。

代码语言:javascript复制
  //设置包下所有类型处理器相关配置
        if (StringUtils.hasLength(this.typeHandlersPackage)) {
            typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; tn");
            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的所有节点配置。

代码语言:javascript复制
      //解析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对象中。

代码语言:javascript复制
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解析(此处不详述 后面会重点分析)

代码语言:javascript复制
//创建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对象

代码语言:javascript复制
 //SqlSessionFactoryBuidler使用Configuration对象来创建SqlSessionFactory并返回 
 this.sqlSessionFactoryBuilder.build(configuration);

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

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

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

0 人点赞