mybatis源码

mybatis源码解析

架构流程图

在这里插入图片描述
说明:

  1. mybatis配置文件
    SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信
    息。
    Mapper.xml,此文件作为mybatis的sql映射文件,文件中配置了操作数据库的sql语句。此
    文件需要在SqlMapConfig.xml中加载。

  2. Configuartion對象

    封裝了Mybatis整个的配置数据(全局配置文件和所有映射文件),这个对象不断向下传递

  3. SqlSessionFactory
    通过mybatis环境等配置信息构造SqlSessionFactory,即会话工厂。SqlSessionFatory加载Configuartion。往下产生SqlSession。SqlSessionFactory由SqlSessionFactoryBuilder(构建者模式)产生,加载配置文件,将数据封装到Configuartion对象中,创建SqlSessionFatory,将Configuartion对象传递给SqlSessionFactory对象。

  4. sqlSession
    通过会话工厂创建sqlSession即会话,程序员通过sqlsession会话接口对数据库进行增删改查操作。一次SQL调用。Session通过SqlSessionFactory工厂产生。

  5. Executor执行器
    mybatis底层自定义了Executor执行器接口来具体操作数据库,Executor接口有两个实现,一个是基本执行器(默认)、一个是缓存执行器,sqlsession底层是通过executor接口操作数据库的。

    执行JDBC代码,Configuration传递过来。

  6. MappedStatement
    它也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个select\insert\update\delete标签对应一个Mapped Statement对象,select\insert\update\delete标签的id即是Mapped statement的id。MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

​ 大白话:映射文件中一个CRUD(select、insert等)标签对应的数据对象,所以MappedStatement对象中会封装Sql语句、参数类型、输出结果类型、statement类型。JDBC中有三种statement对象,statement、PreparedStatement、CallableStatement

类图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. SqlNode

    解析所有的SqlNode信息。举例:就是把xml一个select中的Sql信息放入不同的SqlNode中

  2. IfNode

    解析xml中带if标签的

  3. TextSqlNode

    解析文本SQL,带有#{},${} 举例:select * from user where id=#{id}

  4. staticTestSqlNode

    解析纯文本SQL 举例:select * from user where id=10

  5. MixedSqlNode

    以集合的方式存储所有的SqlNode,意思就是 ifNode、TextSqlNode、staticTextSqlNode都以集合的方式存在MixedSqlNode中

  6. SqlSource

    提供Sql信息的存储和解析功能

  7. DynamicSqlSource

    封装了带有动态标签获取${}信息的SQL语句,也有带有#{}的

  8. RawSqlSource

    封装了只包含#{}信息的SQL语句

  9. StaticSqlSource

    封装了RawSqlSource,通过SqlSource可以获取BoundSQL(存储解析之后的SQL语句)

10.BoundSql

​ 封装解析之后的SQL信息,以及解析占位符?时产生的参数信息

11.TokenHandler

​ 解析#{},${}的interface

12.ParameterMappingTokenHandler

​ 将sql#{}替换为占位符?,将id=#{} 中的 id参数名存储到list集合中,做对象映射

​ 13.GenericTokenParser

​ 通用处理解析sql,解析解析${}和#{},返回sql语句

​ 14.XMLConfigBuilder

​ 专门用来解析全局配置文件的解析器

​ 15.XMLMapperBuilder

​ 专门用来解析映射文件的解析器

​ 16.SimpleTypeRegistry

​ 储存基本类型的,在设置参数时判断是否是基本类型

​ 17.ParameterMapping

​ 从#{}中解析出来的参数信息,包括参数名称和类型

​ 18.MappedStatement

​ 用来封装映射文件中的CRUD标签脚本内容,比如select标签

手写Mybatis流程

简单流程

​ SQL解析流程,不是一边执行,一遍解析,而是需要一次性的完成配置文件,将所有解析出来的数据封装到Configuartion对象中。

SQL解析流程(从配置文件中获取JDBC需要的数据信息)
			1.完成全局配置文件的读取和解析工作,最终将解析出来的信息,封装到【Configuration】对象中
				a)运行时环境信息,其实在此指的就是DataSource的配置信息,将DataSource封装到Configuration对象中存储。
				b)在加载全局配置文件的时候,就会触发映射文件的加载。
				c)映射文件的加载,先去针对每个select标签进行解析,获取【id值(statementId),SQL语句、参数类型、结果类型、statement类型】
					最终将解析出来的信息,封装到【MappedStatement】对象,
					将该对象封装Map集合中,key为statementId,value就是该对象,然后就集合存储到【Configuration】对象中
				d)MappedStatement对象中存储Sql信息,是通过【SqlSource】进行存储的。
					 SqlSource对象,不只是存储Sql信息,而且还提供对存储的SQL信息进行处理的功能。
				e)SqlSource是通过一个SqlNode集合数据来封装的SQL信息。

SQL执行流程(使用JDBC代码,加上从配置文件中读取的SQL相关信息,就可以完成CRUD的执行流程)
	
	【获取数据源对象(dirvername、URL、username、password)提升创建Connection的性能】
	 	【获取Configuartion对象,从该对象中获取DataSource对象】
		【有了DataSource对象,从该对象中获取Conneciton对象】
	【获取JDBC可以执行的SQL语句,此处获取的是BoundSql,此时调用的就是【SQLSource】的SQL解析功能】
	【从BoundSql中获取SQL语句,BoundSql中拿出来的就是解析后的SQL,加上#{}前面的参数名】
	【从MappedStatement对象中获取statement类型,simple、prepared、callable】
	【根据不同的statement去创建不同的statement对象】
		preparedStatement = connection.prepareStatement(sql);
	【从BoundSql中获取参数集合信息List<ParamterMapping>】
	【遍历给参数赋值、先需要读取ParamterMapping中的属性名称从入参对象中获取执行属性的值】
	【调用JDBC代码,完成属性的赋值】
	【执行statement,并获取ResultSet】
		// 向数据库发出 sql 执行查询,查询出结果集
		rs = preparedStatement.executeQuery();
	【从MappedStatement对象中获取输出结果类型、也就是结果要映射的类型】
	【遍历结果集、获取结果集中每一行的数据】
	【获取结果集中每一行每一列的数据,获取列的名称】
	【根据列的名称、通过反射、去设置要映射的java类型的指定属性值】
		// 遍历查询结果集
		while (rs.next()) {
			System.out.println(rs.getString("id") + " " + rs.getString("username"));
		}
				

细化流程

a) 加载配置文件(XML如何配置,直接参考mybatis的配置方式)
	* 【解析全局配置文件】
		** 数据源信息(dirvername、URL、username、password) --最终解析出DataSource对象,并封装到COnfiguartion对象中。
		** 映射文件的信息
	* 【解析映射文件】SqlSource和SqlNode,都是为了去存储数据,并且对存储的数据提供了一些操作。
		** 解析select标签(statement标签) ---封装【MappedStatement】对象,传入id、parameterType、ResultType、statementType
		** 解析select标签中的SQL信息(script)---封装成[SQLSource对象]----》【SQLNode】
			*** DynamicSqlSource和RawSqlSource区别:
				**** DynamicSqlSource的SqlNode集合信息【解析】工作是发生在【每一次调用getBoundSql方法的时候】,另外DynamicSqlSource解析的是带${}的。
				**** RawSqlSource(#{}替换为?占位符)的SQLNode集合信息【解析】工作是发生在【第一次构造RawSqlSource的时候】,只需要被解析一次

b) JDBC执行代码(如果需要配置文件的信息,需要从【Configuartion】对象中获取)
	* 【获取连接】
		** 使用数据源对象去优化连接的创建(DBCP,需要添加第三方的依赖),并且将该信息配置到XML文件中
		** 需要通过Configuration对象,去获取解析出来的【DataSource】对象信息
	* 【获取SQL** 拼接SQL语句:将所有的【SqlNode】(【SqlSource】--->SqlNode集合)中保存的信息,都【拼接】到StringBuffer对象中。在拼接的过程中,会将【${}】解决掉。
			*** SqlNode中包含SqlNode,SqlNode中包含SqlNode集合
				
		** 解析SQL语句:将完成的SQL语句中的【#{}】处理掉,在解析之后,会形成两部分数据【最终的SQL语句、#{}中的属性名称】,将信息封装到StaticSqlSource中
		** 通过SqlSource获取BoundSql(jdbc可执行的SQl语句,参数信息集合)
		** 通过【BoundSql】,获取里面存储的SQL语句
			*** 【BoundSql】将最终的【SQL语句】和【该SQL语句中解析出来的参数信息】,绑定到一起,方便后边一起使用。
			select * from user where id  =#{id} and name = #{name}
						
            BoundSql(
                select * from user where id  = ? and name = ?
                List<ParameterMapping>    
                id和name这两个【参数名称】
                除了参数名称,还需要封装【参数类型】
            )
	* 【创建Statement】
		  ** 通过【MappedStatement】对象获取statementType
	* 【设置参数】
		  ** 通过【MappedStatement】对象获取入参类型(简单类型、引用类型)
		  ** 如果是引用类型,则需要SQL解析过程中,产生的参数信息(ParameterMapping集合信息),我要根据这个参数信息,去入参对象获取指定属性值
		  ** 调用statment.setString(1,"zhangsan")
	* 【执行Statement】
	* 【处理结果】
		  ** 通过【MappedStatement】对象输出映射类型
		  ** 通过反射给输出映射类型对应的对象,去设置属性值(通过resultSet结果集中的每一列中来)

源码阅读

解析阶段

【SqlSessionFactoryBuilder】
|–【XMLConfigBuilder】#new
|–【XMLConfigBuilder】#parse
|–parseConfiguration
|—xxxxxx
|—environmentsElement
|—mapperElement
|–【XMLMapperBuilder】#new
|–【XMLMapperBuilder】#parse
|–configurationElement
|–xxxxxx
|–buildStatementFromContext
|–【XMLStatementBuilder】#new
|–【XMLStatementBuilder】#parseStatementNode
|–【XMLLanguageDriver】#createSqlSource
|–【XMLScriptBuilder】#new
|–【XMLScriptBuilder】#parseScriptNode
|—parseDynamicTags
|—DynamicSqlSource#new
|—RawSqlSource#new
|–【MapperBuilderAssistant】#addMappedStatement:封装MappedStatement

【SqlSessionFactoryBuilder】:工厂构建类
	|--【XMLConfigBuilder】#new 全局配置文件解析类 ,创建了Configuartion对象, 并通过TypeAliasRegistry注册一些Mybatis内部相关类的别名
	|--【XMLConfigBuilder】#parse  使用XPATH解析XML配置文件,将配置文件封装为Configuration对象,返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)
		|--parseConfiguration: 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
		|--environmentsElement:解析</environments>标签 将数据源放入DataSourceFactory接口中,实现类有三个,
		*UnpooledDataSource:非数据库连接池的数据源,每次获取数据库Connection对象都会创建,不会使用数据库连接池
		*PooledDataSource:数据库连接池数据源
		*JndiDataSource:通过容器获取数据源
将DataSourceFactory中的datasource放入Environment构建者类中,最后将id,transactionFactory,dataSource放入全局Configuartion中。
		|--mapperElement 解析</mappers>标签
			|--【XMLMapperBuilder】#new 用来解析mapper映射类
			|--【XMLMapperBuilder】#parse 通过XMLMapperBuilder解析mapper映射文件
				|--configurationElement	从映射文件中的<mapper>根标签开始解析,直到完整的解析完毕
					|--buildStatementFromContext 解析<select>\<insert>\<update>\<delete>子标签
						|--【XMLStatementBuilder】#new MappedStatement解析器
						|--【XMLStatementBuilder】#parseStatementNode  解析select等4个标签,创建MappedStatement对象
							|--【XMLLanguageDriver】#createSqlSource 创建SqlSource,解析SQL,封装SQL语句(未参数绑定)和入参信息
								|--【XMLScriptBuilder】#new  初始化了动态SQL标签处理器
								|--【XMLScriptBuilder】#parseScriptNode // 解析动态SQL
									|---parseDynamicTags  将动态SQL标签中的SQL信息分别封装到不同的SqlNode中,NodeHandler 使用了策略模式
										|---TextSqlNode		解析文本SQL,带有#{},${}
                                        |---StaticTextSqlNode 存入纯文本sql
                                        |---IfNode	带有if标签的
										|---xxxx
                                    |---DynamicSqlSource#new 封装了带有动态标签获取${}信息的SQL语句,也有带有#{}|---RawSqlSource#new  封装了只包含#{}信息的SQL语句
							|--【MapperBuilderAssistant】#addMappedStatement:封装MappedStatement   将MappedStatement对象存储到Configuration中的Map集合中,key为statement的id,value为MappedStatement对象
|-- build 	 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)						

创建sqlSession

【DefaultSqlSessionFactory】#openSession
|–openSessionFromDataSource
|—getEnvironment
|—getTransactionFactoryFromEnvironment
|—transactionFactory.newTransaction
|–configuration.newExecutor
|–DefaultSqlSession#new

【DefaultSqlSessionFactory】#openSession
	 |--openSessionFromDataSource  获取数据源环境对象
	 	|---getEnvironment	获取事物工厂
		 |---getTransactionFactoryFromEnvironment	获取JdbcTransaction或者ManagedTransaction
		 |---transactionFactory.newTransaction 创建Executor执行器
		 |--configuration.newExecutor  创建Executor执行器
		 |--DefaultSqlSession#new   创建DefaultSqlSession

selectOne跟踪

SqlSession#selectOne
|–selectList
|–CachingExecutor#query
|–BaseExecutor#query
|–queryFromDatabase
|–SimpleExecutor#doQuery
|-- Configuration configuration = ms.getConfiguration(); 获取Configuartion对象
|-- prepareStatement(handler, ms.getStatementLog()); 设置参数
|-- 【DefaultParameterHandler】#setParameters 设置参数映射
|-- 【StatementHandler】.query(stmt, resultHandler); 执行SQL语句(已经设置过参数),并且映射结果
|–【PreparedStatementHandler】#query 执行PreparedStatement,也就是执行SQL语句 处理结果集
|–DefaultResultSetHandler#createResultObject

SqlSession#selectOne
	|--selectList
		|--CachingExecutor#query RowBounds是用来逻辑分页(按照条件将数据从数据库查询到内存中,在内存中进行分页), wrapCollection(parameter)是用来装饰集合或者数组参数
			|--CachingExecutor#query  先查询CachingExecutor二级缓存 二级缓存没有 查询BaseExecutor一级缓存,没有从queryFromDatabase数据库查询
				|--queryFromDatabase  从数据库查询
					|--【SimpleExecutor】#doQuery  执行查询  
						|-- Configuration configuration = ms.getConfiguration(); 获取Configuartion对象
						|-- prepareStatement(handler, ms.getStatementLog()); 设置参数
							|-- 【DefaultParameterHandler】#setParameters 设置参数映射
						|-- 【StatementHandler】.query(stmt, resultHandler); 执行SQL语句(已经设置过参数),并且映射结果集
							|--【PreparedStatementHandler】#query  执行PreparedStatement,也就是执行SQL语句 处理结果集
								|--ResultSetHandler#handleResultSets 处理结果集
									|--DefaultResultSetHandler#createResultObject 创建要映射的PO类对象
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值