MyBatis学习笔记


MyBatis 学习笔记

一、为什么要用 MyBatis ?

一些持久化框架对比

方式 优点 缺点 应用场景
Hibernate 不再需要编写 SQL 就可以通过映射关系来操作数据库 当多表关联超过 3 个时 Hibernate 的级联会损失很多性能;学习成本高 适合性能要求不太苛刻的系统,不适合需要大量复杂查询的系统
Spring JDBC 内嵌 Spring 框架中、支持 AOP ;提供了统一的异常处理,框架处理了异常;事务管理 只是对原生 JDBC 进行一层非常薄的封装,没有缓存 需要在代码中嵌入 SQL 语句,适用中小型项目
MyBatis 满足灵活定制 SQL 和性能优化的需求 编写 SQL 和映射规则,工作量相对大些 性能要求高、响应快、灵活的系统;SQL 修改、优化比较方便

二、MyBatis 概念梳理

  1. MyBatis 是支持普通 SQL 查询、存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的查询。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects)映射成数据库中的记录。
  2. 与 JDBC 相比,MyBatis 简化了相关代码,SQL 语句在一行代码中就能执行,MyBatis 提供了一个映射引擎,声明式的将 SQL 语句的执行结果与对象数据进行映射,通过使用一种内建的类 XML 表达式语言,SQL 语句可以被动态生成。
  3. MyBatis 支持声明式数据缓存(declarative data caching)。当一条 SQL 语句被标记为“可缓存”后,首次执行它时从数据库获取的所有数据会被缓存在高速缓存中,后面再执行这条语句时,就会从高速缓存中读取结果,而不是再次从数据库中获取。

下图是 MyBatis 功能流程的整体架构图,主要包含了接口层、核心层、基础层。

  • 接口层主要指的是 session 。
  • 核心层主要指的是 MyBatis 的配置、SQL 执行、结果映射等。
  • 基础层主要包含日志、缓存、连接池、事务等。

MyBatis 整体架构图

三、MyBatis 缓存原理梳理

1、一级缓存

一级缓存:当在一次数据库会话中,执行多次查询条件完全相同的 SQL 时,MyBatis 提供了一级缓存的方案优化,如果是相同的 SQL 语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。每个 SqlSession 中持有了 Executor ,每个 Executor 中有一个 LocalCache 。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement ,在 LocalCache 进行查询,如果缓存命中的话,直接返回结果给用户,见下图。

一级缓存

如果缓存没有命中的话,查询数据库,结果写入 LocalCache ,最后返回结果给用户,见下图。

一级缓存

其中 SqlSession 提供用户和数据库交互的所有方法,隐藏了底层的细节。

Executor :虽然 SqlSession 向用户提供了操作数据库的方法,但真正和数据打交道的是 Executor 。

Cache :MyBatis 中的 Cache 接口,提供了和缓存相关的最基本的操作。

  1. MyBatis 对会话(Session)级别的一级缓存设计的比较简单,就简单地使用了 HashMap 来维护,并没有对 HashMap 的容量和大小进行限制。
  2. 一级缓存是一个粗粒度的缓存,没有更新缓存缓存过期的概念。

2、二级缓存

二级缓存:在一级缓存中最大的共享范围就是在一个 SqlSession 内部,如果需要在多个 SqlSession 之间共享缓存就需要使用二级缓存。开启二级缓存后,会使用 CachingExecutor 装饰 Executor ,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示。

二级缓存

二级缓存开启后,同一个 namespace 下的所有 SQL 语句都是共用同一个 Cache ,即二级缓存被多个 SqlSession 共享。

当开启缓存后,查询数据会经过二级缓存 -> 一级缓存 -> 数据库

  1. MyBatis 的二级缓存相对于一级缓存来说,实现了 SqlSession 之间缓存数据的共享,同时粒度更加的细,能够到 namespace 级别,通过 Cache 接口实现类不同的组合,对 Cache 的可控性也更强。
  2. 当需要多表查询时,使用 MyBatis 的二级缓存极大可能会造成脏数据。因为 MyBatis 的二级缓存是基于 namespace 的,且每次 update 缓存都会被更新,当其他 namespace 中有语句对所在表进行修改时,多表查询语句所在的 namspace 无法感应到这些修改,就会造成脏数据。
  3. 在分布式环境下,由于默认的 MyBatis Cache 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis 的 Cache 接口实现,有一定的开发成本,直接使用 Redis、Memcached 等分布式缓存可能成本更低,安全性也更高。

怎样判断两次查询是相同的查询?

  • 传入的 statementId ;
  • 查询时要求的结果集中的结果范围 (结果的范围通过 rowBounds.offset 和 rowBounds.limit 表示);
  • 这次查询所产生的最终要传递给 JDBC java.sql.Preparedstatement 的 SQL 语句字符串;
  • 传递给 java.sql.Statement 要设置的参数值。

Statement Id + Offset + Limit + Sql + Params

四、Spring 与 MyBatis 整合

MyBatis 与 Spring 整合后的整体框架依赖图如下图所示,这里需要引入 mybatis-spring 依赖。

Spring 整合 MyBatis

整合前整合后的使用对比
MyBatis Spring-MyBatis
通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory 。 通过 SqlSessionFactoryBean 创建 SqlSessionFactory,SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口。
通过 SqlSessionFactory.openSession() 方法来创建 SqlSession 实例。 通过 SqlSessionTemplate 管理 MyBatis 的 SqlSession 。
通过映射器接口的方式来使用 XML 中对应的 SQL 。 通过 MapperFactoryBean 给每个接口创建一个代理,并将其变成 Spring 的一个 Bean 。
通过 MapperScannerConfigurer 扫描将包下的所有接口自动都注册一个 MapperFactoryBean 。 @MapperScan 注解取代 MapperScannerConfigurer 。
MyBatis 事务接口 Transaction ,它有两个实现类:JdbcTransaction :通过使用 JDBC 提供的方式来管理事务;ManagedTransaction:通过容器来进行事务管理。 整合后事务委托给了 Spring ,Spring 使用 PlatformTransactionManager 接口当做一个事务管理器,它的两个实现类: DataSourceTransactionManager :用于支持本地事务 JtaTransactionManager :用于支持分布式事务

五、FAQ

1、MyBatis 是怎么分页的?

MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,可以在 SQL 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL ,然后重写 SQL ,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

2、MyBatis 插件机制是什么原理?

MyBatis 采用责任链模式,通过对下面四个接口的对象生成动态代理对象来实现的,借助这些插件可以改变 MyBatis 的默认行为(诸如 SQL 重写之类的)。

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

发出请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

MyBatis 允许使用插件来拦截的有四个接口:

  • Executor(内部执行器,可以借助插件实现自定义二级缓存)
  • StatementHandler( JDBC 封装层,可以借助插件实现自定义一级缓存)
  • ResultSetHandler(结果集处理,可以借助插件对 MyBatis 的结果集进行修改)
  • ParameterHandler(可以借助插件改变 SQL 的参数默认设置)

MyBatis 插件机制

3、MyBatis 如何支持延迟加载的?

MyBatis 的延迟加载功能默认是关闭的。

  1. 需要在 SqlMapConfig.xml 文件中通过 setting 标签配置来开启延迟加载功能。
  2. 开启延迟加载的属性:
    lazyLoadingEnabled :全局性设置懒加载。如果设为 “false” ,则所有相关联的都会被初始化加载。默认为 false 。
    aggressiveLazyLoading :当设置为 “true” 的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。默认为 true 。
  3. 在 resultMap 中使用 association 或者 collection 就可以使用延迟加载。association 指的是一对一,collection 指的是一对多查询。
  4. 延迟加载主要是通过动态代理的形式实现,通过代理拦截到指定方法,执行数据加载。

文章作者: wenjun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wenjun !
评论
 上一篇
团灭股票买卖问题 团灭股票买卖问题
团灭股票买卖问题读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目: 买卖股票的最佳时机 买卖股票的最佳时机 II 买卖股票的最佳时机 III 买卖股票的最佳时机 IV 最佳买卖股票时机含冷冻期 买卖股票的最佳时
2020-06-17
下一篇 
Spring常见问题总结 Spring常见问题总结
Spring 常见问题总结1. 什么是 Spring 框架?Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说 Spring 框架指的都是 Spring Framework ,它是很多模块的集合,
2020-06-07
  目录