Mybatis的核心架构及源码解读

 更新时间:2023年08月25日 09:58:52   作者:抠脚的大灰狼  
这篇文章主要介绍了Mybatis的核心架构及源码解读,mybatis是一款半自动化的持久层框架,它封装了JDBC操作,支持定制化SQL,高级映射,但它的数据库无关性较低,需要的朋友可以参考下

概述

mybatis是什么?

mybatis是一款半自动化的持久层框架,它封装了JDBC操作,支持定制化SQL,高级映射。但它的数据库无关性较低,2个不同的数据库,可能需要2套SQL语句

mybatis的基本使用?

  • 编写全局配置文件
  • 编写mapper映射文件
  • 加载配置文件,生成SqlSessionFactory
  • 创建SqlSession,通过SqlSession调用mapper映射文件中的SQL语句来执行数据库操作

架构流程

三层结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LfGIOhPg-1573726013288)(C:\Users\Vergi\Desktop\3level-architech.png)]

接口层

使用SqlSession和Mapper接口,来完成对SQL语句的调用,日常开发中主要接触这一层

数据处理层

这一层是mybatis进行的工作,负责SQL语句组装,查询参数绑定,结果集映射

基础支撑层

这一层可以理解为我们全局配置里的内容。包括数据库连接信息,事务管理信息,配置缓存,编写mapper映射文件中的SQL语句等

工作流程

  • 向SqlSession传入SQL语句的id,以及查询参数
  • 找到待执行的SQL信息,交给Executor执行器处理
  • Executor负责对SQL语句进行组装拼接,后交给StatementHandler处理
  • StatementHandler封装了JDBC的操作,它负责根据SQL信息,生成对应的Statement,并利用ParameterHandler进行查询参数的解析与绑定,后执行查询
  • StatemenHandler查询完毕,将结果集交由ResultSetHandler进行结果集信息的解析与封装处理(参数解析,结果集解析,都会用TypeHandler来做类型转换,java类型与JDBC类型)

源码部分

全局配置文件解析过程

  • 获得配置文件的InputStream,创建Document对象
  • 利用Xpath语法,解析各个配置节点
  • 将信息封装到Configuration对象中,生成SqlSessionFactory

源码过程

SqlSessionFactoryBuilder # build 
 |- XMLConfigBuilder # parse 
   |- XMLConfigBuilder # parseConfiguration

mapper映射文件解析过程

  • 一个mapper.xml映射文件,由namespace属性作为唯一标识
  • 拥有namespace属性的mapper.xml映射文件,会被注册到Configuration中的mapperRegistry中,以便后续生成mapper代理对象
  • 一个mapper.xml,对应一个MapperBuilderAssistant对象,这个builderAssistant对象解析并保存了该mapper.xml中的公共标签,如parameterMap,resultMap,cache,sql,这些标签可能在某个CRUD标签里被使用
  • 解析CRUD标签,即 select | update | insert | delete 标签,一个CRUD标签,被封装成一个MappedStatement对象,以标签的id属性作为唯一标识,MappedStatement里包含了SQL语句信息,参数映射信息,结果集映射信息

源码过程

XMLConfigBuilder # mapperElement
 |- XMLMapperBuilder # parse
   |- XMLMapperBuilder # configurationElement

SQL加载与组装过程

SQL装载

  • 在解析mapper映射文件中的CRUD标签时,对SQL语句进行了解析和封装
  • 将一个CRUD标签,封装成SqlNode,并将其子元素(可能是文本节点,也可能是动态SQL节点),也封装成SqlNode,利用组合模式,对这些SqlNode进行组装,最终将SqlNode和Configuration封装在一起,形成SqlSource
  • 有动态SQL标签的,或者有${} 的,会被封装成DynamicSqlSource,其余的,会被封装成RawSqlSource(在Executor执行时都会解析并封装成StaticSqlSource)
  • SqlSource和其他信息,一起被封装为MapperStatement,一个CRUD标签,对应一个MappedStatement

源码过程

XMLMapperBuilder # buildStatementFromContext
 |- XMLStatementBuilder # parseStatementNode
   |- XMLLanguageDriver # createSqlSource 
     |- XMLScriptBuilder # parseScriptNode

SQL组装

  • 调用Executor进行执行时,会查找对应的MappedStatement,并调用其SqlSource的getBoundSql,进行SQL语句的组装,并封装查询参数
  • 调用getBoundSql方法时,会调用SqlNode的apply方法,不同SqlNode子类,会采取不同方式,解析动态SQL标签,并进行SQL语句拼接,并将#{}替换为 ? ,将${}做字符串拼接,之后封装到StaticSqlSource,此时已经将SQL语句解析并组装,这个StaticSqlSource里就是SQL语句以及查询参数

源码过程

CachingExecutor # query
 |- MappedStatement # getBoundSql
  |- DynamicSqlSource # getBoundSql
   |- SqlNode # apply // 动态SQL的组装,以及将${}进行字符串拼接
   |- SqlSourceBuilder # parse //这里是将#{}替换成 ?
   //组装完成后封装成StaticSqlSource
   //并调用StaticSqlSource的getBoundSql
   //new 一个新的BoundSql,传入组装好的SQL语句,以及查询参数

执行查询过程

  • 从Configuration中根据id,取出一个MappedStatement
  • 将MappedStatement交由Executor处理
  • Executor中调用MappedStatement的getBoundSql,获取组装好的SQL语句,以及查询参数
  • 根据查询参数,MappedStatement等信息,构建出一个StatementHandler出来
  • StatementHandler新建一个Statement对象,并借助ParameterHandler完成对Statement的入参绑定
  • 执行查询,并将结果集交由ResultSetHandler处理

源码过程

DefaultSqlSession # selectList
 |- CachingExecutor # query
   |- BaseExecutor # query
     |- BaseExecutor # queryFromDatabase
       |- SimpleExecutor # doQuery
         |- Configuration # newStatementHandler
         |- SimpleExecutor # prepareStatement
         |- StatementHandler # query

缓存过程

  • 执行查询时,首先是走CachingExecutor,CachingExecutor中检查是否开启二级缓存,若开启,则会负责二级缓存数据的存取
  • 若没开启二级缓存,或二级缓存没命中,进入到BaseExecutor中,尝试从一级缓存中拿数据,若一级缓存中也没有,则会访问数据库
  • 二级缓存是mapper级别的,即一个mapper,对应一个二级缓存。在源码中,二级缓存是被MappedStatement持有。二级缓存是通过mapper映射文件中的<cache/> 标签开启的
  • 一级缓存无法关闭,但可以在全局配置中设置<setting name="localCacheScope" value="STATEMENT"/> 来使其失效(每次执行操作都会清空一级缓存)

源码过程

//二级缓存
CachingExecutor # query
 |- MappedStatement # getCache//获取该mapper下的二级缓存
 |- TransactionalCacheManager # getObject //查找缓存中是否有数据
   |- TransactionalCache # getObject //查找缓存中是否有数据
 |-TransactionalCacheManager # putObject //存入二级缓存
   |- TransactionalCache # getObject
//若二级缓存未命中,走一级缓存
BaseExecutor # query
  |- PerpetualCache # getObject //从一级缓存中取数据

延迟加载过程

  • 在查询结束后,调用ResultSetHandler对结果集进行处理时,若发现开启了延迟加载,且有嵌套查询,则会对结果生成一个代理对象
  • 当调用结果的get方法,访问延迟加载的数据时,发现数据为空,则获取其MappedStatement,执行一次查询,把查询结果set到主对象中

源码过程

DefaultResultSetHandler # createResultObject
 |- Configuration # getProxyFactory
 |- JavassistProxyFactory # createProxy
 // 默认是使用JavassistProxyFactory

获取Mapper代理过程

  • 调用Configuration中的mapperRegistry来查找这个mapper的类信息,会找到一个MapperProxyFactory对象
  • 使用JDK内置的动态代理,调用java.lang.reflect下的Proxy来生成一个代理类

源码过程

DefaultSqlSession # getMapper
 |- MapperRegistry # getMapper
  |- MapperProxyFactory # newInstance
    |- MapperProxy # 构造函数
    |- Proxy.newProxyInstance

mybatis插件过程

mybatis的插件会对以下几个类起作用

  • Executor
  • StatementHandler
  • ParameterHandler
  • ResultSetHandler

实现Interceptor接口,覆写intercept方法,在intercept方法中完成插件的拦截逻辑。

并覆写plugin方法,在plugin方法中调用Plugin.wrap来生成一个代理对象并返回,即可。

底层也是使用的JDK动态代理

源码流程

//以Executor为例
DefaultSqlSessionFactory # openSession
  |- Configuration # newExecutor
    |- InterceptorChain # pluginAll

类关系总结

配置文件解析相关

  • BaseBuilder
  • XMLConfigBuilder
  • XMLMapperBuilder
  • 持有一个MapperBuilderAssistant
  • XMLStatementBuilder
  • XMLScriptBuilder

SQL组装相关

  • SqlSource
    • DynamicSqlSource :
      • 含有动态SQL,或 ${} ,会被解析封装成这个类
    • RawSqlSource :
      • 不含动态SQL,以及${} ,会被解析封装成这个类
    • StaticSqlSource
      • Executor执行查询,调用getBoundSql方法时,组装好SQL语句,与查询参数一同封装起来,为这个类
  • SqlNode
    • TextSqlNode 文本节点
    • StaticTextSqlNode 文本节点,且文本不包含 ${}
    • IfSqlNode
    • ForEachSqlNode
    • ChooseSqlNode
    • TrimSqlNode
    • 有2个子类,分别是
      • WhereSqlNode
      • SetSqlNode
    • MixedSqlNode作为根节点,其有一个 List<SqlNode> 字段

执行相关

  • Executor

    • BaseExecutor

      其有3个子类,分别是

      • SimpleExecutor
      • ReuseExecutor
      • BatchExecutor
    • CachingExecutor

  • StatementHandler
    • RoutingStatementHandler

      仅作路由选择功能

    • BaseStatementHandler

      其有3个子类,分别是

      • SimpleStatementHandler
      • PreparedStatementHandler
      • CallableStatementHandler
  • ParameterHandler

  • ResultSetHandler

  • Cache

    • PerpetualCache
    • LruCache
    • FifoCache
    • SerializedCache
    • LoggingCache
    • SynchronizedCache
    • SoftCache
    • WeakCache
    • TransactionalCache

到此这篇关于Mybatis的核心架构及源码解读的文章就介绍到这了,更多相关Mybatis的核心架构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java中int转string与string转int的效率对比

    java中int转string与string转int的效率对比

    这篇文章主要介绍了java中int转string与string转int的效率对比,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java中StringBuilder与StringBuffer使用及源码解读

    Java中StringBuilder与StringBuffer使用及源码解读

    我们前面学习的String就属于不可变字符串,因为理论上一个String字符串一旦定义好,其内容就不可再被改变,但实际上,还有另一种可变字符串,包括StringBuilder和StringBuffer两个类,那可变字符串有什么特点,又怎么使用呢,接下来就请大家跟我一起来学习吧
    2023-05-05
  • 详解使用Spring3 实现用户登录以及权限认证

    详解使用Spring3 实现用户登录以及权限认证

    这篇文章主要介绍了详解使用Spring3 实现用户登录以及权限认证,这里整理了详细的代码,有需要的小伙伴可以参考下。
    2017-03-03
  • java实现简易连连看小游戏

    java实现简易连连看小游戏

    这篇文章主要为大家详细介绍了java实现简易连连看小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 关于@ComponentScan TypeFilter自定义指定扫描bean的规则

    关于@ComponentScan TypeFilter自定义指定扫描bean的规则

    这篇文章主要介绍了关于@ComponentScan TypeFilter自定义指定扫描bean的规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • IDEA 2020.3.X 创建scala环境的详细教程

    IDEA 2020.3.X 创建scala环境的详细教程

    这篇文章主要介绍了IDEA 2020.3.X 创建scala环境的详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • 深入理解java中Arrays.sort()的用法

    深入理解java中Arrays.sort()的用法

    这篇文章主要介绍了深入理解java中Arrays.sort()的用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • java中以DES的方式实现对称加密并提供密钥的实例

    java中以DES的方式实现对称加密并提供密钥的实例

    这篇文章主要介绍了java中以DES的方式实现对称加密并提供密钥的实例的相关资料,这里提供实例帮助大家学习理解这部分知识,需要的朋友可以参考下
    2017-08-08
  • 解决HttpServletRequest 流数据不可重复读的操作

    解决HttpServletRequest 流数据不可重复读的操作

    这篇文章主要介绍了解决HttpServletRequest 流数据不可重复读的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • tomcat connection-timeout连接超时源码解析

    tomcat connection-timeout连接超时源码解析

    这篇文章主要为大家介绍了tomcat connection-timeout连接超时源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论