Mybatis源码分析(二)---SqlSessionFactory获取

2023-10-14 11:20

本文主要是介绍Mybatis源码分析(二)---SqlSessionFactory获取,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SqlSessionFactory和SqlSession的获取

  • 一. SqlSessionFactory获取
    • ①. SqlSessionFactoryBuilder类中的build()方法
    • ②. 创建XML配置解析器---XMLConfigBuilder
    • ③. XMLConfigBuilder类中的parse()方法
    • ④. XMLConfigBuilder类中的parseConfiguration()方法
    • ⑤. XMLConfigBuilder类中的mapperElement()方法
    • ⑥. XMLMapperBuilder类中的parse()方法
    • ⑦. XMLMapperBuilder类中的configurationElement()方法
    • ⑧. XMLMapperBuilder类中的buildStatementFromContext()方法
    • ⑨. XMLStatementBuilder类中的parseStatementNode()方法
    • ⑩. MapperBuilderAssistant类中的addMappedStatement()方法
    • 十一. 创建DefaultSqlSessionFactory对象
  • 二. SqlSessionFactory构建流程图
  • 三. XMLConfigBuilder构建流程图
  • 四. XMLMapperBuilder构建流程图
  • 五. Configuration构建流程图
  • 六. XMLStatementBuilder构建流程图

Mybatis启动解析过程图

在这里插入图片描述

一. SqlSessionFactory获取

在测试类中找到SqlSessionFactory获取入口

在这里插入图片描述

①. SqlSessionFactoryBuilder类中的build()方法

在这里插入图片描述

  public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// 1. 创建XML配置解析器XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);// 2.1. parser.parse(): 解析配置文件,创建配置类Configuration// 2.2. build(): 创建SqlSessionFactory对象,并返回return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {}}}// 3. 创建DefaultSqlSessionFactory对象public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

build()方法流程总结

  1. 创建XML配置解析器
  2. 解析配置文件
  3. 创建DefaultSqlSessionFactory对象

②. 创建XML配置解析器—XMLConfigBuilder

在这里插入图片描述

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {//1. 参数配置commonConstructor(validation, variables, entityResolver);//2. 将配置信息输入流封装为Document对象, 以便后面进行解析this.document = createDocument(new InputSource(inputStream));}

③. XMLConfigBuilder类中的parse()方法

在这里插入图片描述

  public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}// mybatis配置文件解析的主流程:// 1. parser.evalNode("/configuration") --> 获取到根节点// 2. 根据根标签<configuration>开始解析parsed = true;// 解析配置文件中根标签下的所有子标签parseConfiguration(parser.evalNode("/configuration"));return configuration;}

④. XMLConfigBuilder类中的parseConfiguration()方法

在这里插入图片描述

 private void parseConfiguration(XNode root) {try {// 1. 解析properties节点propertiesElement(root.evalNode("properties"));// 2. 解析settings节点Properties settings = settingsAsProperties(root.evalNode("settings"));// 3. VFS主要用来加载容器内的各种资源,比如jar或者class文件loadCustomVfs(settings);loadCustomLogImpl(settings);// 4. 解析类型别名typeAliasesElementtypeAliasesElement(root.evalNode("typeAliases"));// 5. 加载插件pluginElement// 比如: 分页插件PageHelper,再比如druid连接池提供的各种监控、拦截、预发检查功能,pluginElement(root.evalNode("plugins"));//  6. 加载对象工厂objectFactoryElementobjectFactoryElement(root.evalNode("objectFactory"));// 7. 创建对象包装器工厂objectWrapperFactoryElementobjectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));// 8. 加载反射工厂reflectorFactoryElementreflectorFactoryElement(root.evalNode("reflectorFactory"));// 到setting之后,调用settingsElement(Properties props)将各值赋值给configuration,settingsElement(settings);// 9. 加载环境配置environmentsElementenvironmentsElement(root.evalNode("environments"));// 10. 数据库厂商标识加载databaseIdProviderElement(了解即可)databaseIdProviderElement(root.evalNode("databaseIdProvider"));// 11. 加载类型处理器typeHandlerElementtypeHandlerElement(root.evalNode("typeHandlers"));// 12. 加载mapper文件 或 mapperElement  --->  (重点)mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}

⑤. XMLConfigBuilder类中的mapperElement()方法

在这里插入图片描述

private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {//如果配置包扫描if ("package".equals(child.getName())) {//获取需要扫描的包路径String mapperPackage = child.getStringAttribute("name");//解析包信息, 注册该包下的Mappersconfiguration.addMappers(mapperPackage);} else {//获取resource,url,mapperClass属性的值String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");//resource属性解析if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);try(InputStream inputStream = Resources.getResourceAsStream(resource)) {XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());mapperParser.parse();}} else if (resource == null && url != null && mapperClass == null) {//url属性解析ErrorContext.instance().resource(url);try(InputStream inputStream = Resources.getUrlAsStream(url)){XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse();}} else if (resource == null && url == null && mapperClass != null) {//mapperClass属性解析Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}

⑥. XMLMapperBuilder类中的parse()方法

在这里插入图片描述

  public void parse() {if (!configuration.isResourceLoaded(resource)) {//  解析mapper.xml中的<mapper></mapper>标签configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);// 根据接口创建MapperProxyFactory工厂bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}

⑦. XMLMapperBuilder类中的configurationElement()方法

在这里插入图片描述

private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.isEmpty()) {throw new BuilderException("Mapper's namespace cannot be empty");}//设置名称空间builderAssistant.setCurrentNamespace(namespace);//开始对mapper.xml中各个标签进行解析// 1. 解析缓存映射<cache-ref></cache-ref>cacheRefElement(context.evalNode("cache-ref"));// 2. 解析缓存<cache></cache>cacheElement(context.evalNode("cache"));// 3. 解析参数映射<parameterMap></parameterMap>parameterMapElement(context.evalNodes("/mapper/parameterMap"));// 4. 解析结果集映射<resultMap></resultMap>resultMapElements(context.evalNodes("/mapper/resultMap"));//  5. 解析<sql></sql>sqlElement(context.evalNodes("/mapper/sql"));// 6. 解析CRUD语句<select></select> |<insert></insert> |<update></update> |<delete></delete>  (重点)buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);}}

⑧. XMLMapperBuilder类中的buildStatementFromContext()方法

  private void buildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) {buildStatementFromContext(list, configuration.getDatabaseId());}buildStatementFromContext(list, null);}private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {//遍历解析每条sql语句for (XNode context : list) {//用每个sql标签的上下文对象创建statementParser解析器final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try {//解析SQL节点statementParser.parseStatementNode();} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}}}

⑨. XMLStatementBuilder类中的parseStatementNode()方法

创建statementParser解析器, 调用parseStatementNode()对标签进行解析

在这里插入图片描述

 public void parseStatementNode() {/***     <!--配置查询所有-->*     <select id="getUserList" resultType="com.xizi.pojo.User">*         select * from user*     </select>**/String id = context.getStringAttribute("id");String databaseId = context.getStringAttribute("databaseId");if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;}..........................................................................................// 将解析内容封装到MappedStatement中builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}

parseStatementNode()方法解析步骤

  1. 对之前解析好的各种属性进行解析配置
  2. 将所有的sql配置进行封装

⑩. MapperBuilderAssistant类中的addMappedStatement()方法

在这里插入图片描述

 public MappedStatement addMappedStatement(String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class<?> parameterType,String resultMap,Class<?> resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang,String resultSets) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved");}//接口路径 + 方法名称id = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);  //二级缓存配置ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap);}MappedStatement statement = statementBuilder.build();// 将解析好的statement实例加入mappedStatements集合中configuration.addMappedStatement(statement);return statement;

十一. 创建DefaultSqlSessionFactory对象

在这里插入图片描述

  // 3. 创建DefaultSqlSessionFactory对象public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

二. SqlSessionFactory构建流程图

在这里插入图片描述

三. XMLConfigBuilder构建流程图

在这里插入图片描述

四. XMLMapperBuilder构建流程图

在这里插入图片描述

五. Configuration构建流程图

在这里插入图片描述

六. XMLStatementBuilder构建流程图

在这里插入图片描述

这篇关于Mybatis源码分析(二)---SqlSessionFactory获取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/210163

相关文章

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

MyBatis ResultMap 的基本用法示例详解

《MyBatisResultMap的基本用法示例详解》在MyBatis中,resultMap用于定义数据库查询结果到Java对象属性的映射关系,本文给大家介绍MyBatisResultMap的基本... 目录MyBATis 中的 resultMap1. resultMap 的基本语法2. 简单的 resul

Mybatis的分页实现方式

《Mybatis的分页实现方式》MyBatis的分页实现方式主要有以下几种,每种方式适用于不同的场景,且在性能、灵活性和代码侵入性上有所差异,对Mybatis的分页实现方式感兴趣的朋友一起看看吧... 目录​1. 原生 SQL 分页(物理分页)​​2. RowBounds 分页(逻辑分页)​​3. Page

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Mybatis Plus Join使用方法示例详解

《MybatisPlusJoin使用方法示例详解》:本文主要介绍MybatisPlusJoin使用方法示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录1、pom文件2、yaml配置文件3、分页插件4、示例代码:5、测试代码6、和PageHelper结合6

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三

MyBatis设计SQL返回布尔值(Boolean)的常见方法

《MyBatis设计SQL返回布尔值(Boolean)的常见方法》这篇文章主要为大家详细介绍了MyBatis设计SQL返回布尔值(Boolean)的几种常见方法,文中的示例代码讲解详细,感兴趣的小伙伴... 目录方案一:使用COUNT查询存在性(推荐)方案二:条件表达式直接返回布尔方案三:存在性检查(EXI

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau