大家好,又见面了,我是你们的朋友全栈君。
缘起:笔者从业一年有余(此处撸撸胡须,摸摸自己半秃的头发)不经发出感叹:天天写ssm框架搭建下的业务框架,对于数据库操作也使用mybatis。但是扪心自问对于mybatis的使用只是停留在表层,从来没有研究过其原理,于是本着只有变秃才能变强的原则,于是准备研究一下mybatis的框架并再次做出记录。这里笔者对于mybatis的源码分析是在spring环境下进行的。
这是笔者第一个研究的框架源码,分析不准确的,请大家及时评论,互相交流,共同进步。
再次立个flag,以后多分析源码并发表一个比之前有深度的it文章(最次两周写一篇博文)
目录
2、SqlSessionFactoryBean的getObject()方法
2.1、在spring环境获取SqlSeesionFactory对象
2.3、buildSqlSessionFactory()方法
本篇使用的框架环境是Spring 4.1.7 mybatis 3.3.0
主要针对SqlsessionFactory对象在spring环境中的初始化过程,1、包括SqlSessionFactoryBean对象的配置
2、mybatis.config.xml(mybatis配置文件的解析),3、mybatis配置中节点的注册到Configuration对象中。
4、最终返回SqlSessionFactory对象
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>
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
分析该类实现了三个接口
实现该接口是spring中的工厂bean 与spring中的普通bean不同,该工厂bean返回的是其生产出来的T对象,而非工厂本身 需要实现的方法getObject() 即返回SqlSessionFactory对象。(补充一点这个也是我们进行分析源码的入口)
关于FactoryBean<T> 可以参考:https://www.cnblogs.com/quanyongan/p/4133724.html
在spring容器中管理的bean 在初始化的过程中会执行其接口中的afterPropertiesSet()方法
详细解释参考: https://blog.csdn.net/z69183787/article/details/78415201
用来监听spring容器的相关事件 SqlSessionFactoryBean监听ContextRefreshedEvent 事件,该事件会在Spring容器初始化完成会触发该事件(暂时还不清楚)
补充知识: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文件(可以单独设置也可以用扫描包)。
调用该类的afterPropertiesSet()方法
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
构造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();
}
各位这个是重点啊,是我们需要重点关注的东西,在这里进行详细的讲解所以将其分成几个部分来分别进行讲解(当然这里也是管中窥豹,只是将主要的流程进行大致的梳理,对其详细解释在后面的部分进行讲解)。
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进行数据增删改查的操作。
手打不易,看过觉得不错请点赞,谢谢
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/136767.html原文链接:https://javaforall.cn