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分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

mybatis映射器配置小结

《mybatis映射器配置小结》本文详解MyBatis映射器配置,重点讲解字段映射的三种解决方案(别名、自动驼峰映射、resultMap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定... 目录select中字段的映射问题使用SQL语句中的别名功能使用mapUnderscoreToCame

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

mybatis-plus如何根据任意字段saveOrUpdateBatch

《mybatis-plus如何根据任意字段saveOrUpdateBatch》MyBatisPlussaveOrUpdateBatch默认按主键判断操作类型,若需按其他唯一字段(如agentId、pe... 目录使用场景方法源码方法改造首先在service层定义接口service层接口实现总结使用场景my

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

MyBatis-Plus 与 Spring Boot 集成原理实战示例

《MyBatis-Plus与SpringBoot集成原理实战示例》MyBatis-Plus通过自动配置与核心组件集成SpringBoot实现零配置,提供分页、逻辑删除等插件化功能,增强MyBa... 目录 一、MyBATis-Plus 简介 二、集成方式(Spring Boot)1. 引入依赖 三、核心机制

MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)

《MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)》本文给大家介绍MyBatis的xml中字符串类型判空与非字符串类型判空处理方式,本文给大家介绍的非常详细,对大家的学习或... 目录完整 Hutool 写法版本对比优化为什么status变成Long?为什么 price 没事?怎