本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。欢迎转载,转载请注明出处(https://blog.csdn.net/feng_xiaoshi/article/details/106268334),谢谢。
文章目录
简介
使用 Mybatis 是否有 Mybatis 的核心成分是什么?它在做什么?Mybatis 仅添加了接口类,没有实现类,为什么可以直接查询?等等的疑问,今天就深入 Mybatis 代码,探索 MyBatis 世界。
MyBatis设计框架
代码框架
抽象框架
- 接口层
- 数据处理层
- 底层支撑层
- 启动层
核心组件
类名 | 作用 |
---|---|
SqlSession | MyBatis 关键 API,和数据库交互会话,执行数据库crud操作的核心 |
Executor | MyBatis 执行器,是 MyBatis 调度协调程序的核心,负责 SQL 语句的生成和查询缓存的维护 |
StatementHandler | 封装JDBC语句操作,负责JDBC语句的操作,例如如何设置参数,将执行结果集放入列表集合中。 |
ParameterHandler | 参数处理器,负责处理接口的入参的参数,以转换 Statement JDBC 所需的参数, |
ResultSetHandler | 返回结果集处理器,负责将 JDBC 返回的 ResultSet 结果集对象转换为 List 类型的集合。 |
TypeHandler | 负责Java数据类型和JDBC数据类型之间的映射和转换,主要由其子对象实现,例如 StringHandler 负责处理 String 类型转换 |
MappedStatement | MappedStatement 负责 select 、update 、 delete 、insert 标签的封装, |
SqlSource | 根据调用者的 parameterObject 实现转换,动态生成SQL语句,将信息封装到 BoundSql 对象,然后返回。 |
BoundSql | 表示动态生成的SQL语句以及相应的参数信息 |
Configuration | MyBatis的所有配置信息都保存在Configuration对象中。 |
运行原理
分析流程图
创建 SqlSessionFactory 和 SqlSession
创建 MapperProxy
执行 sql 操作
分析解剖
现在进行代码分析、逻辑解剖:
整个测试流程总共分为三步,分别对应上面流程图:
第一步:构建 SqlSessionFactoryBuilder ,通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory 对象,进而通过 SqlSessionFactory 创建 SqlSession 对象,此过程中,每个解析的文件都存储在 Configuration 中,返回的 SqlSessionFactory 属于 DefaultSqlSessionFactory,在创建 SqlSession 对象过程中将创建一个Executor对象,还涉及 MappedStatement 包含 insert、deleted、update 的详细信息。
第二步:构建接口的代理对象 MapperProxy,通过 MapperProxyFactory 进行创建代理对象,包含代理对象。
第三步:执行增删改查方法,此过程中涉及 BoundSql 对 sql 生成。
第一步
InputStream inputStream = Resources.getResourceAsStream(resource);
读取到的输入流传入 build 方法中,生成 DefaultSqlSessionFactory 对象。
调用 build 方法逻辑:
XMLConfigBuilder 配置构建器:
分析 XMLConfigBuilder 源码可以发现在 mybatis 的注释中,包含了 issue 修改编号的记录,针对 117 编号的 issue 找到了问题原因,而且由于后来代码维护和修改,代码的实际逻辑和 issue 的问题出入很大,而且针对 issue 631 的问题与代码不符,如果有哪位师兄知道具体原因可以留言一起探究。
调用 build() 方法,获取 DefaultSqlSessionFactory:
SqlSessionFactoryBuilder 提供了大量的 build() 方法,主要是供配置信息转换到 mybatis 上下文当中,供 BoundSql 、SqlSession 获取需要的信息。
DefaultSqlSessionFactory#openSession() :
调用获取新执行器的方法:
返回的 DefaultSqlSession 包含了全局配置和执行器:
第二步
第二步的关注点是如何通过反射来获取 MapperProxy,一起走进代码逻辑:
首先是 DefaultSqlSession 调用 getMapper 方法:
这个方法调用的是全局配置的 getMapper 方法:
Configuration#getMapper 调用 MapperRegistry#getMapper:
在 MapperRegistry 方法中通过创建 MapperProxyFactory 来构建一个 MapperProxy 实例,MapperProxy 是一个 InvocationHandler,InvocationHandler 作用是: Mybatis 初始化的时候扫描包路径,以及配置路径,以及如何将 Java Mapper 和 XML Mapper 映射起来, 原理就是使用 JDK 动态代理(cglib) 构建代理类,InvocationHandler就是这个JDK 动态代理。
MapperProxyFactory 创建 MapperProxy:
注意: 代码中调用 Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
此方法返回指定的接口的代理类的实例方法调用指派到指定的调用处理程序,因此 Mybatis 通过反射将 Mapper 接口和对应的 XML 配置文件信息代理一个对应的 Mapper 接口的实体类。
MapperProxy 源码:
第三步
有了对应的接口实体,下一步就是执行对应的 Sql 语句。
调用 MapperProxy#invoke,生成代理对象就是执行 invoke方法,才会执行相应的被代理对象的方法:
execute方法的逻辑:
重点分析 selectOne 方法的逻辑:
在代码中可以发现 selectOne 调用了 selectList ,判断 selectList 返回的数据条数大于 1 抛出异常,如果为空抛出 null
selectList 查询,进入 XML 配置中 sql 语句执行的核心:
DynamicSqlSource#getBoundSql(Object parameterObject)
方法,返回一个 BoundSql 对象。一个 BoundSql 对象,代表了一次sql语句的实际执行,而 SqlSource 对象的责任,就是根据传入的参数对象,动态计算出这个 BoundSql。
BaseExector: 基础执行器,封装了子类的公共方法,包括一级缓存、延迟加载、回滚、关闭等功能.
CahceKey 中 key 的格式:影响因子的 HashCode1:影响因子的 HashCode2:方法ID:offset:limit:SQL语句:参数:环境ID
-1123117942:1738479101:com.feng.UserMapper.selectUser:0:2147483647:select * from user where id = ? :1:emma:0:dev
通过路由 RoutingStatementHandler 来获取真实使用的 StatementHandler。
总结
这篇文章只是简单描述一下 Mybatis 的运行原理,以及类的作用说明,当然还有许多细节和类没有介绍,Mybatis中的设计模式和架构思路都是值得学习。