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

相关文章

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

MyBatis-Plus 中 nested() 与 and() 方法详解(最佳实践场景)

《MyBatis-Plus中nested()与and()方法详解(最佳实践场景)》在MyBatis-Plus的条件构造器中,nested()和and()都是用于构建复杂查询条件的关键方法,但... 目录MyBATis-Plus 中nested()与and()方法详解一、核心区别对比二、方法详解1.and()

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

MySQL 获取字符串长度及注意事项

《MySQL获取字符串长度及注意事项》本文通过实例代码给大家介绍MySQL获取字符串长度及注意事项,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 获取字符串长度详解 核心长度函数对比⚠️ 六大关键注意事项1. 字符编码决定字节长度2

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,