一篇文章告诉你JAVA Mybatis框架的核心原理到底有多重要

目录
  • 持久层的那些事
    • 什么是 JDBC
    • JDBC 原理
    • 什么是 Mybatis
    • Mybatis 与 JDBC 的关系
      • SqlSession
      • SqlSessionFactory
      • SqlSessionFactoryBuilder
      • Configuration
      • MappedStatement
      • Executor
      • ParameterHandler
      • StatementHandler
      • ResultSetHandler
      • Interceptor
  • Mybatis 关键词说明
  • Mybatis 架构设计
    • 基础支持层
      • 反射模块
      • 类型转换
      • 日志模块
      • 资源加载
      • 解析器模块
      • 核心处理层
      • 问题答疑
    • Mybatis 中分页如何实现
      • SQL 分页
      • 拦截器分页
  • 总结

持久层的那些事

什么是 JDBC

JDBC(JavaDataBase Connectivity)就是 Java 数据库连接, 说的直白点就是 使用 Java 语言操作数据库

本来我们是通过控制台或客户端操作的数据库, JDBC 是用 Java 语言来发送 SQL 语句

JDBC 原理

最初 SUN 公司希望提供一套 能够适用所有数据库的 API, 但是在实际操作中却发现这是项基本不可能完成的任务

因为各个厂商所提供的 数据库差异实在太大, 所以 SUN 公司与数据库厂商讨论出的就是:由 SUN 公司提供出一套访问数据库的规范 API, 并提供相对应的连接数据库协议标准, 然后各厂商根据规范提供一套访问自家数据库的 API 接口

最终:SUN 公司提供的规范 API 称之为 JDBC, 各厂商提供的自家数据库 API 接口称之为 驱动

什么是 Mybatis

Mybatis 是一款优秀的 ORM(持久层)框架,使用 Java 语言 编写前身是 apache 的一个开源项目 iBatis,2010 年迁移到 google code 并正式改名为 Mybatis ORM 持久层 指的是 : 将业务数据存储到磁盘,也具备长期存储能力,只要磁盘不损坏,如果在断电情况下,重启系统仍然可以读取数据

Mybatis 与 JDBC 的关系

在没有持久层框架之前, 想要代码中操作数据库都必须通过 JDBC 来操作, 接下来一个例子来说明两者之间的关系

JDBC 操作数据库

相信大家都在实际项目中使用过 Mybatis, 可以联想一下, 平常我们工作中, 是否做过以下事情:

  • 是否装载过数据库驱动?
  • 是否从驱动中获取数据库连接?
  • 是否创建过执行 SQL 的 Statement?
  • 是否自行将数据库返回结果转换成 Java 对象?
  • 是否关闭过 finally 块中的三个对象?

看到上面的灵魂拷问, 就可以对本次分享的第一个问题作出解答:

Mybatis 针对 JDBC 中重复操作做了封装, 同时扩展并优化部分功能

Mybatis 关键词说明

📖 如果在阅读文章前没有接触过 Mybatis 源码相关的内容, 建议将下述名词多看几遍再向下阅读

SqlSession

负责执行 select、insert、update、delete 等命令, 同时负责获取映射器和管理事务; 其底层封装了与 JDBC 的交互, 可以说是 mybatis 最核心的接口之一

SqlSessionFactory

负责创建 SqlSession 的工厂, 一旦被创建就应该在应用运行期间一直存在, 不需要额外再进行创建

SqlSessionFactoryBuilder

主要是负责创建 SqlSessionFactory 的构造器类, 其中使用到了构建者设计模式; 仅负责创建 SqlSessionFactory

Configuration

Mybatis 最重要的配置类, 没有之一, 存储了大量的对象配置, 可以看源码感受一下

MappedStatement

MappedStatement 是保存 SQL 语句的数据结构, 其中的类属性都是由解析 .xml 文件中的 SQL 标签转化而成

Executor

SqlSession 对象对应一个 Executor, Executor 对象作用于 增删改查方法 以及 事务、缓存 等操作

ParameterHandler

Mybatis 中的 参数处理器, 类关系比较简单

StatementHandler

StatementHandler 是 Mybatis 负责 创建 Statement 的处理器, 根据不同的业务创建不同功能的 Statement

ResultSetHandler

ResultSetHandler 是 Mybatis 负责将 JDBC 返回数据进行解析, 并包装为 Java 中对应数据结构的处理器

Interceptor

Interceptor 为 Mybatis 中定义公共拦截器的接口, 其中定义了相关实现方法

Mybatis 架构设计

架构图

基础支持层

反射模块

反射在 Java 中的应用可以说是相当广泛了, 同时也是一把双刃剑。 Mybatis 框架本身 封装出了反射模块, 提供了比原生反射更 简洁易用的 API 接口, 以及对 类的元数据增加缓存, 提高反射的性能

类型转换

类型转换模块最重要的功能就是在为 SQL 语句绑定实参时, 将 Java 类型转为 JDBC 类型, 在映射结果集时再由 JDBC 类型转为 Java 类型

另外一个功能就是提供别名机制, 简化了配置文件的定义

日志模块

日志对于系统的作用不言而喻, 尤其是测试、生产环境上查看信息及排查错误等都非常重要。主流的日志框架包括 Log4j、Log4j2、S l f4j 等, Mybatis 的日志模块作用就是 集成这些日志框架

资源加载

Mybatis 对类加载器进行了封装, 用来确定类加载器的使用顺序, 用来记载类文件以及其它资源文件, 感兴趣可以参考 ClassLoaderWrapper

解析器模块

解析器模块主要提供了两个功能, 一个是封装了 XPath 类, 在 Mybatis 初始化时解析 Mybatis-config.xml 配置文件以及映射配置文件提供功能, 另一点就是处理动态 SQL 语句的占位符提供帮助

核心处理层

配置解析

在 Mybatis 初始化时, 会加载 Mybatis-config.xml 文件中的配置信息, 解析后的配置信息会 转换成 Java 对象添加到 Configuration 对象

📖 比如说在 .xml 中定义的 resultMap 标签, 会被解析为 ResultMap 对象

SQL 解析

大家如果手动拼写过复杂 SQL 语句, 就会明白会有多痛苦。Mybatis 提供出了动态 SQL, 加入了许多判断循环型标签, 比如 : if、where、foreach、set 等, 帮助开发者节约了大量的 SQL 拼写时间 SQL 解析模块的作用就是将 Mybatis 提供的动态 SQL 标签解析为带占位符的 SQL 语句, 并在后期将实参对占位符进行替换

SQL 执行

SQL 的执行过程涉及几个比较重要的对象, ExecutorStatementHandlerParameterHandlerResultSetHandler

Executor负责维护 一级、二级缓存以及事务提交回滚操作, 举个查询的例子, 查询请求会由 Executor 交给 StatementHandler 完成

StatementHandler 通过ParameterHandler完成SQL语句的实参绑定, 通过java.sql.Statement执行 SQL语句并拿到对应的 结果集映射

最后交由 ResultSetHandler 对结果集进行解析, 将 JDBC 类型转换为程序自定义的对象

插件

插件模块是 Mybatis 提供的一层扩展, 可以针对 SQL 执行的四大对象进行 拦截并执行自定义插件插件编写需要很熟悉 Mybatis 运行机制, 这样才能控制编写的插件安全、高效

接口层

接口层只是 Mybatis 提供给调用端的一个接口 SqlSession, 调用端在进行调用接口中方法时, 会调用核心处理层相对应的模块来完成数据库操作

问题答疑

.xml 文件定义 Sql 语句如何解析

Mybatis 在创建 SqlSessionFactory 时, XMLConfigBuilder 会解析 Mybatis-config.xml 配置文件

Mybatis 相关解析器

Mybatis 解析器模块中定义了相关解析器的抽象类 BaseBuilder, 不同的子类负责实现解析不同的功能, 使用了 Builder 设计模式

XMLConfigBuilder 负责解析 mybatis-config.xml 配置文件

XMLMapperBuilder 负责解析业务产生的 xxxMapper.xml

mybatis-config.xml 解析

XMLConfigBuilder 解析 mybatis-config.xml 内容参考代码 :

XMLConfifigBuilder#parseConfiguration()方法将 mybatis-config.xml中定义的标签进行相关解析并填充到Configuration对象中

xxxMapper.xml 解析

XMLConfifigBuilder#mapperElement()中解析配置的 mappers 标签, 找到具体的.xml文件, 并将其中的selectinsertupdatedeleteresultMap 等标签解析为 Java 中的对象信息具体解析 xxxMapper.xml 的对象为 XMLMapperBuilder, 具体的解析方法为parse()

Mybatis创建 SqlSessionFactory 会解析mybatis-config.xml, 然后 解析configuration 标签下的子标签, 解析 mappers 标签时, 会根据相关配置读取到 .xml 文件, 继而解析 .xml中各个标签具体的 selectinsertupdatedelete 标签定义为 MappedStatement对象, .xml文件中的其余标签也会根据不同映射解析为 Java 对象

MappedStatement

这里重点说明下 MappedStatement 对象, 一起看一下类中的属性和 SQL 有何关联呢

MappedStatement 对象中 提供的属性与 .xml 文件中定义的 SQL 语句 是能够对应上的, 用来 控制每条 SQL 语句的执行行为

Mapper 接口的存储与实现

在平常我们写的 SSM 框架中, 定义了 Mapper 接口与 .xml 对应的 SQL 文件, 在 Service 层直接注入 xxxMapper 就可以了

也没有看到像 JDBC 操作数据库的操作, Mybatis 在中间是如何为我们省略下这些重复繁琐的操作呢

这里使用 Mybatis 源码中的测试类进行验证, 首先定义 Mapper 接口, 省事直接注解定义 SQL

这里使用 SqlSession 来获取 Mapper 操作数据库, 测试方法如下

创建 SqlSession

#1 从 SqlSessionFactory 中打开一个 新的 SqlSession

获取 Mapper 实例

#2 就存在一个疑问点, 定义的 AutoConstructorMapper 明明是个接口, 为什么可以实例化为对象?

动态代理方法调用

#3 通过创建的对象调用类中具体的方法, 这里具体聊一下 #2 操作

SqlSession 是一个接口, 有一个 默认的实现类 DefaultSqlSession, 类中包含了 Configuration 属性

Mapper 接口的信息以及 .xml 中SQL语句是在Mybatis初始化时添加 到 Configuration MapperRegistry 属性中的

#2中的 getMapper 就是从 MapperRegistry 中获取 Mapper

看一下 MapperRegistry 的类属性都有什么

config 为 保持全局唯一 的 Configuration 对象引用

knownMappers 中 Key-Class 是 Mapper 对象, Value-MapperProxyFactory 是通过 Mapper 对象衍生出的 Mapper 代理工厂

再看一下 MapperProxyFactory 类的结构信息

mapperInterface 属性是 Mapper 对象的引用, methodCache key Mapper 中的方法, value Mapper 解析对应 SQL 产生的 MapperMethod

📖 Mybatis 设计 methodCache 属性时使用到了 懒加载机制, 在初始化时不会增加对应 Method, 而是在 第一次调用时新增

MapperMethod 运行时数据如下, 比较容易理解

通过一个实际例子帮忙理解一下 MapperRegistry 类关系, Mapper 初始化第一次调用的对象状态, 可以看到 methodCache 容量为0

我们目前已经知道 MapperRegistry的类关系, 回头继续看一下第二步的 MapperRegistry#getMapper() 处理步骤

核心处理在 MapperProxyFactory#newInstance()方法中, 继续跟进

MapperProxy继承了 InvocationHandler 接口, 通过 newInstance() 最终返回的是由 Java Proxy 动态代理返回的动态代理实现类

看到这里就清楚了步骤二中接口为什么能够被实例化, 返回的是 接口的动态代理实现类

Mybatis Sql 的执行过程

根据 Mybatis SQL 执行流程图进一步了解

大致可以分为以下几步操作:

📖 在前面的内容中, 知道了Mybatis Mapper 是动态代理的实现, 查看 SQL 执行过程, 就需要紧跟实现InvocationHandler 的MapperProxy类

执行增删改查

@Select(" SELECT * FROM SUBJECT WHERE ID = #{id}")
PrimitiveSubject getSubject(@Param("id") final int id);

我们以上述方法举例, 调用方通过 SqlSession 获取Mapper动态代理对象, 执行Mapper方法时会通过InvocationHandler进行代理

MapperMethod#execute 中, 根据 MapperMethod -> SqlCommand -> SqlCommandType 来确定增、删、改、查方法

📖 SqlCommandType 是一个枚举类型, 对应五种类型 UNKNOWN、INSERT、UPDATE、DELETE、SELECT、FLUSH

参数处理

查询操作对应 SELECT 枚举值, if else 中判断为返回值是否集合、无返回值、单条查询等, 这里以查询单条记录作为入口

Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);

📖 这里能够解释一个之前困扰我的问题, 那就是为什么方法入参只有单个 @Param("id"), 但是参数 param 对象会存在两个键值对

继续查看 SqlSession#selectOne 方法, sqlSession 是一个接口, 具体还是要看实现类 DefaultSqlSession

因为单条和查询多条以及分页查询都是走的一个方法, 所以在查询的过程中, 会将分页的参数进行添加

执行器处理

在 Mybatis 源码中, 创建的执行器默认是 CachingExecutor, 使用了装饰者模式, 在类中保持了 Executor 接口的引用, CachingExecutor 在持有的执行器基础上增加了缓存的功能

delegate.query 就是在具体的执行器了, 默认 SimpleExecutor, query 方法统一在抽象父类 BaseExecutor 中维护

BaseExecutor#queryFromDatabase 方法执行了缓存占位符以及执行具体方法, 并将查询返回数据添加至缓存

BaseExecutor#doQuery 方法是由具体的 SimpleExecutor 实现

执行 SQL

因为我们 SQL 中使用了参数占位符, 使用的是 PreparedStatementHandler 对象, 执行预编译SQL的 Handler, 实际使用 PreparedStatement 进行 SQL 调用

返回数据解析

将 JDBC 返回类型转换为 Java 类型, 根据 resultSets 和 resultMap 进行转换

Mybatis 中分页如何实现

通过 Mybatis 执行分页 SQL 有两种实现方式, 一种是编写 SQL 时添加 LIMIT, 一种是全局处理

SQL 分页

<select id="getSubjectByPage" resultMap="resultAutoMap">
    SELECT * FROM SUBJECT LIMIT #{CURRINDEX} , #{PAGESIZE}
</select>

拦截器分页

上文说到, Mybatis 支持了插件扩展机制, 可以拦截到具体对象的方法以及对应入参级别

我们添加插件时需要实现Interceptor 接口, 然后将插件写在 mybatis-config.xml 配置文件中或者添加相关注解, Mybatis 初始化时解析才能在项目启动时添加到插件容器中

由一个 List 结构存储项目中全部拦截器, 通过Configuration#addInterceptor 方法添加

重点需要关注 Interceptor#pluginAll 中 plugin 方法, Interceptor 只是一个接口, plugin 方法只能由其实现类完成

Plugin 可以理解为是一个工具类, Plugin#wrap 返回的是一个动态代理类

这里使用一个测试的 Demo 看一下方法运行时的参数

虽然是随便写的 Demo, 但是与正式使用的插件并无实际区别

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java经典面试题汇总:Mybatis

    目录 1. MyBatis 中 #{}和 ${}的区别是什么? 2. MyBatis 有几种分页方式? 3. MyBatis 逻辑分页和物理分页的区别是什么? 4. MyBatis 是否支持延迟加载?延迟加载的原理是什么? 5. 说一下 MyBatis 的一级缓存和二级缓存? 6. MyBatis 有哪些执行器(Executor)? 7. MyBatis 分页插件的实现原理是什么? 8. MyBatis如何返回主键? 9. Xml映射文件中,除了常见的select|insert|update|d

  • JAVA MyBatis入门学习过程记录

    目录 一.Mybatis 1.mybatis-config.xml 2.Mapper.xml 3.db.properties 4.MybatisUtils工具类 5.多对一AND多对一: 6.使用注解开发 7.自定义缓存:ehcache 8.mybatis 其他工具 二.MAVEN资源导出错误解决 三.常用依赖导入 四.LOG4J 1.log4j.properties 配置文件 2.log4j 在mybatis-config.xml的配置 3.使用场景 好用的插件: 1.lombok 总结 一.

  • Java面试题冲刺第九天--MyBatis2

    目录 面试题1:说说你对Mybatis的理解? 追问1:说一下MyBatis的工作原理和流程吧. 追问2:列举几个MyBatis的核心组件,说说分别干啥用? 面试题2:(问几个实际使用的问题)Mybatis动态sql是做什么的?都有哪些动态sql? 追问1:Xml映射文件中,除了常见的select|insert|updae|delete标签之外,你还常用哪些标签? 追问2:Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? 追问3:MyBatis中接口绑定你都用过哪几

  • Java面试题冲刺第九天--MyBatis

    目录 面试题1:你怎么理解ORM框架,常见的ORM框架都有哪些? 正经回答: 追问1:大家都在用Mybatis,Mybatis都有哪些优势? 面试题2:相比较Hibernate与Mybatis,你有哪些看法? 正经回答: 面试题3:Mybatis中的#{}和${}有哪些区别 正经回答: 深入追问: 追问1:什么是sql注入? 追问2:mybatis是如何做到防止sql注入的? 总结 面试题1:你怎么理解ORM框架,常见的ORM框架都有哪些? 正经回答: 对象关系映射(Object Relatio

  • Java持久层框架Mybatis入门详细教程

    mybatis介绍 mybatis它是轻量级持久层框架,由ibatis演化而来.它自动连接数据库,将数据库的结果集封装到对象中POJO. POJO: 一个简单的Java类,这个类没有实现/继承任何特殊的java接口或者类,不遵循任何主要java模型,约定或者框架的java对象.在理想情况下,POJO不应该有注解. JavaBean: JavaBean是可序列化的,实现了serializable接口 具有一个无参构造器 有按照命名规范的set和gett,is(可以用于访问布尔类型的属性)方法 My

  • 一篇文章告诉你JAVA Mybatis框架的核心原理到底有多重要

    目录 持久层的那些事 什么是 JDBC JDBC 原理 什么是 Mybatis Mybatis 与 JDBC 的关系 SqlSession SqlSessionFactory SqlSessionFactoryBuilder Configuration MappedStatement Executor ParameterHandler StatementHandler ResultSetHandler Interceptor Mybatis 关键词说明 Mybatis 架构设计 基础支持层 反射

  • Java Mybatis框架入门基础教程

    一.Mybatis介绍 MyBatis是一款一流的支持自定义SQL.存储过程和高级映射的持久化框架.MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去 设置参数和获取检索结果.MyBatis能够使用简单的XML格式或者注解进行来配置,能够映射基本数据元素.Map接口和POJOs(普通java对象)到数据库中的记录. 二.MyBatis工作流程 (1)加载配置并初始化 触发条件:加载配置文件 配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个

  • java mybatis框架配置详解

    一个框架的使用,必然离不开其中的组件支持.我们在下载完mybatis框架后,因为大部分的内部结构还没有启动,就要手动的对其进行配置.在之前有提到,mybatis框架的作用就有数据库方面的,所以本篇文章带来了数据库和sql方面的配置方法,大家一起往下面看看具体操作. 1.配置数据库 创建mybatis的配置文件,配置数据库的信息.数据库我们可以配置多个,但是默认的只能用一个. <?xml version="1.0" encoding="UTF-8"?> &

  • java mybatis框架实现多表关系查询功能

    基于Maven框架的整体设计 -- 一多一的关系 思路:导入mybatis.mysql.Junit4.13依赖: 编写两个java实体类: 编写sqMapConfig.xml mybatis核心配置文件 编写dao层接口: 编写mapper 映射文件: 编写测试类. 1.导入相关依赖 <!--配置依赖--> <dependencies> <!--配置mybatis--> <dependency> <groupId>org.mybatis</

  • Java Mybatis框架多表操作与注解开发详解分析

    目录 一对一查询 多对多查询 Mybatis的注解开发 Mybatis的增删查改 MyBatis的注解实现复杂映射开发 一对一查询 一对一查询的模型 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户. 一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户 一对一查询的语句 对应的sql语句: select * from orders o,user u where o.uid=u.id;查询的结果如下: 创建Order和User实体 创建OrderMapper接口 p

  • Java Mybatis框架Dao层的实现与映射文件以及核心配置文件详解分析

    目录 Mybatis的Dao层实现 传统开发方式 代理开发方式 MyBatis映射文件深入 动态sql语句 动态SQL之<if> 动态SQL之<foreach> SQL片段抽取 总结 Mybatis核心配置文件深入 typeHandlers标签 plugins标签 总结 Mybatis的Dao层实现 传统开发方式 1.编写UserDao接口 public interface UserMapper { public List<User> findAll() throws

  • Java Mybatis框架增删查改与核心配置详解流程与用法

    目录 Mybatis简介 Mybatis开发步骤: Mybatis的映射文件概述 Mybatis的增删改查操作 MyBatis的核心配置文件概述 MyBatis核心配置文件层级关系 MyBatis常用配置解析 Mybatis相应API 原始JDBC操作 原始jdbc操作(查询数据) 原始jdbc操作(插入数据) 原始jdbc操作的分析原始jdbc开发存在的问题如下: ①数据库连接创建.释放频繁造成系统资源浪费从而影响系统性能 ②sql 语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可

  • Java Mybatis框架由浅入深全解析下篇

    目录 前言 什么是Maven Maven环境配置 Maven 构建生命周期 Maven项目的创建 目录结构 pom.xml文件 什么是pom.xml文件 加入项目所需依赖 添加资源文件的指定 总结 前言 上一篇我们第一次测试了Mybatis框架,并且成功了. 本想直接推进学习框架配置,但是很多小伙伴对Maven不了解,今天就来浅谈一下Maven. 今天我们就来剖析pom.xml配置文件,这个pom.xml文件,是我们构建maven项目的配置文件,既然我们使用到了,就利用本篇文章学习一下吧.这里只

  • Java Mybatis框架由浅入深全解析中篇

    目录 前言 添加框架的步骤 在idea中添加数据库的可视化 添加jdbc.properties属性文件(数据库配置) 添加SqlMapCongig.xml 创建实体类Student用来封装数据 添加增删改查 创建测试类进行功能测试 总结 前言 上一篇我们了解了框架相关知识,并且导入依赖配置了核心文件,今天就可以开始写代码测试了. 添加框架的步骤 在idea中添加数据库的可视化 这里需要注意:很多小伙伴链接不成功,这个时候要修改一下自己的驱动版本,尽量与数据库版本一致 添加jdbc.propert

  • Java Mybatis框架由浅入深全解析上篇

    目录 学习路线 什么是三层架构 常用的SSM框架(了解) 什么是框架 什么是Mybatis框架 添加框架的步骤 1.新建库建表 2.新建maven项目 3.修改目录 4.修改pom.xml文件 5.修改pom.xml文件 总结 学习路线 什么是三层架构 在项目开发中,遵循一种形式模式,分为三层. 界面层: 用来接收客 户端的输入,调用业务逻辑层进行功能处理,返回结果给客户端.过去的servlet就是界面层的功能. **业务逻辑层:**用来进行整个项目的业务逻辑处理,向上为界面层提供处理结果,向下

随机推荐