Mybatis 是一个非常小巧灵活的 ORM 框架,是为了消除所有的 JDBC 代码和参数的手工设置以及结果集的封装问题;基于这一点可以知道 MyBatis 实际上就是对 JDBC 的封装。
提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理,Mybatis 提供了两种方式
下面的这些方法就是在 SqlSession 接口中提供的接口方法
/**
* Retrieve a single row mapped from the statement key and parameter.
* @param <T> the returned object type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return Mapped object
*/
<T> T selectOne(String statement, Object parameter);
/**
* Retrieve a list of mapped objects from the statement key and parameter.
* @param <E> the returned list element type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return List of mapped object
*/
<E> List<E> selectList(String statement, Object parameter);
/**
* Execute an insert statement with the given parameter object. Any generated
* autoincrement values or selectKey entries will modify the given parameter
* object properties. Only the number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @param parameter A parameter object to pass to the statement.
* @return int The number of rows affected by the insert.
*/
int insert(String statement, Object parameter);
/**
* Execute an update statement. The number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @param parameter A parameter object to pass to the statement.
* @return int The number of rows affected by the update.
*/
int update(String statement, Object parameter);
/**
* Execute a delete statement. The number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @param parameter A parameter object to pass to the statement.
* @return int The number of rows affected by the delete.
*/
int delete(String statement, Object parameter);
// 此处省略一万字....Mapper 代理的方式就是通过创建接口(UserMapper 接口)的代理对象调用接口中的方法,最终执行方法调用是 Executor 对象,底层其实还是调用的传统的 API 方法
数据处理层这部分主要就 JDBC 核心内容,包括参数映射、类型转换、SQL 解析、SQL 执行以及结果的封装,这部分内容就可以理解成JDBC 的核心内容,这里有几个重要类
MyBatis 执行器,用于执行增删改查操作这部分主要是框架抽取出来的通用组件包括数据源管理、事务管理、配置加载和缓存处理;为上层的数据处理层提供最基础的支撑
数据源的管理对于 持久层 框架来说,可谓是非常的重要,稍有不慎就会浪费大量系统资源,MyBatis 也对此做了很好的支持,MyBatis有三种内建的数据源类型 UNPOOLED、POOLED、JNDI
JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI上下文的数据源引用。这种已经不用了通常情况下是会使用 POOLED ,可以节省很多宝贵的系统资源
在 MyBatis 中有两种类型的事务管理器 ( JDBC / MANAGED ) 它通过一个顶层的 Transaction 接口以及其不同实现 JdbcTransaction 和 ManagedTransaction 来实现对事务的管理:
JdbcTransaction对事务的管理,在和 Spring 整合之后,通常会用 Spring 的事务管理器
缓存对于持久层框架来说还是非常重要的,一定程度上可以减少和数据库的交互,MyBatis 提供了两种缓存机制,一级缓存和二级缓存
SqlSession级别的缓存,也就是会话级别的,如果两个相同的查询,第二次的查询会直接先从缓存中去拿,一级缓存也是默认开启的,这部分在源码分析的内容里会详细分析Mapper 级别的缓存,也就是一个xxx.xml 内的查询是可以共用的,需要手动开启MyBatis 支持两种 SQL 解析的方式 一种 xml、一种是注解;
两种方式对于一些基础的 CRUD 区别不大,使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句
是 MyBatis 启动时加载核心配置的方式,严格来说也可以不算在架构层面,不过 MyBatis 也是提供了两种方式一个是 xml 的方式,也是用的比较多的,一种是使用 Java API 的方式
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<!--省略-->
</environment>
</environments>
</configuration>Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);一般情况下还是使用 xml 的比较方便
MyBatis 是对 JDBC 的封装,JDBC 执行六步走
Connection 连接SQL分析下 MyBatis 是怎样来封装这个过程的
读取 MyBatis 的核心配置文件。mybatis-config.xml 是 MyBatis 全局配置文件,用于配置数据库连接、属性、类型别名、类型处理器、插件、环境配置、映射器(mapper.xml)等信息,这个核心配置文件最终会被封装成一个 Configuration 对象
加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,映射文件是在 mybatis-config.xml 中加载;可以加载多个映射文件,每个文件对应数据库中的一张表。
配置的方式有两种
<!-- 使用包路径,扫描包下所有的接口,这种方式比较方便 -->
<package name="com.mybatis.demo"/>
<!-- resource:使用相对路径的资源引用-->
<!-- url:使用绝对类路径的资源引用-->
<!-- class:使用映射器接口实现类的完全限定类名-->
<mapper resource="xxx.xml"/>构造会话工厂获取 SqlSessionFactory 。这个过程是用建造者设计模式使用 SqlSessionFactoryBuilder 对象构建的,SqlSessionFactory 的最佳作用域是应用作用域
创建会话对象 SqlSession。由会话工厂创建 SqlSession 对象,对象中包含了执行 SQL 语句的所有方法,每个线程都应该有它自己的 SqlSession 实例。SqlSession 实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
Executor 执行器是 MyBatis 的核心,负责 SQL 语句的生成和查询缓存的维护,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护
SimpleExecutor – SIMPLE 就是普通的执行器ReuseExecutor -执行器会重用预处理语句(PreparedStatements)BatchExecutor --它是批处理执行器MappedStatement 对象是对解析的 SQL 的语句封装,一个 MappedStatement 代表了一个 sql 语句标签
<select id="selectUserList" resultType="com.mybatis.User">
select * from t_user
</select>输入参数映射。输入参数类型可以是基本数据类型,也可以是 Map、List、POJO 类型复杂数据类型,这个过程类似于 JDBC 的预编译处理参数的过程
封装结果集。可以封装成多种类型可以是基本数据类型,也可以是 Map、List、POJO 类型复杂数据类型。封装结果集的过程就和JDBC封装结果集是一样的
这样一对比,就可以看出了其实 Mybatis 的执行流程和 JDBC 其实大同小异。如果用代码表示如下:
// 1.加载配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 创建SqlSessionFactory对象实际创建的是DefaultSqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 创建SqlSession对象实际创建的是DefaultSqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 创建代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 5. 执行查询语句
List<User> users = mapper.selectUserList();总结 MyBatis 执行流程
| 特性 | 旧版(3.4.x 及以前) | 当前(3.5.x) |
|---|---|---|
| JDK 支持 | JDK 6+ | JDK 8+(阻塞式调用可直接运行于虚拟线程) |
| 动态 SQL | if/trim/foreach 等标签齐备 | 不变 |
| 参数/结果映射 | 普通 POJO | 支持 record 构造器自动映射(3.5.10+ argNameBasedConstructorAutoMapping) |
| 插件机制 | Interceptor 拦截 StatementHandler 等 | 不变,新增 @Intercepts 校验增强 |
| 延迟加载 | 需配置 lazyLoadingEnabled | 行为不变;aggressiveLazyLoading 自 3.4.1 起默认 false |
整体架构(SqlSessionFactory → SqlSession → Executor → MappedStatement → StatementHandler)与执行流程在 3.5.x 中保持不变,重点在于 JDK 兼容性与 record 等新类型支持。