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获取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/weixin_45480785/article/details/116935762
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/210163

相关文章

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

《SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能》:本文主要介绍SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能,本文分步骤给大家介绍的... 目录原理解析1. mysql主从复制(Master-Slave Replication)2. 读写分离3.

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

微信公众号脚本-获取热搜自动新建草稿并发布文章

《微信公众号脚本-获取热搜自动新建草稿并发布文章》本来想写一个自动化发布微信公众号的小绿书的脚本,但是微信公众号官网没有小绿书的接口,那就写一个获取热搜微信普通文章的脚本吧,:本文主要介绍微信公众... 目录介绍思路前期准备环境要求获取接口token获取热搜获取热搜数据下载热搜图片给图片加上标题文字上传图片