Mybatis源码解析:SQL执行流程

请添加图片描述

各种Executor的作用

在前面的文章中我们演示了通过SqlSession来执行sql,其实SqlSession只是一个api类,主要是为了方便用户的使用,典型的门面模式
在这里插入图片描述
Executor通过操作StatementHandler来执行sql,并返回结果,因此Executor起到了一个承上启下的作用
在这里插入图片描述
在mybatis中最重要的组件有如下4个

Executor:驱动sql执行
StatementHandler:调用Statement执行sql
ParameterHandler:给执行的sql设置参数
ResultSetHandler:将返回的ResultSet封装为Java对象

Executor的类的继承关系如下图所示
在这里插入图片描述
Executor的设计是一个典型的装饰者模式,SimpleExecutor,ReuseExecutor是具体实现类,而CachingExecutor是装饰器类

类型作用
SimpleExecutor默认配置,使用PreparedStatement对象访问数据库,每次访问都要创建新的PreparedStatement对象
ReuseExecutor使用PreparedStatement对象访问数据库,访问时会重用statement对象
BatchExecutor批量执行增删改sql
BaseExecutor用来维护一级缓存
CachingExecutor用来维护二级缓存

其中BaseExecutor的设计又用到了模版模式,例如query方法封装了操作一级缓存的内容,而其实现类会重写doQuery方法,到数据库中去查询数据

后面我们会详细分析一级缓存和二级缓存,先演示一下三种具体Executor的区别

public class UserInfo {

  private Integer id;

  private String name;

  private Integer age;
}
public interface UserInfoMapper {

  @Select("select * from user_info WHERE id = #{id}")
  UserInfo selectById(int id);

  @Update("update user_info set name = #{name} where id = #{id}")
  int updateNameById(String name, Integer id);
}

mybatis-config.xml
在这里插入图片描述
初始化环境测试环境
在这里插入图片描述
测试SimpleExecutor
在这里插入图片描述
在这里插入图片描述
从日志可以看到SimpleExecutor每次都会重新编译Statement

测试ReuseExecutor
在这里插入图片描述
在这里插入图片描述
从日志可以看到ReuseExecutor会重用Statement,并不会每次重新编译Statement

测试BatchExecutor
BatchExecutor是用来批量执行增删改sql的
在这里插入图片描述
当执行完上面的Test后你会发现更改并未生效,你需要手动刷行一下,如下所示
在这里插入图片描述
假如你执行了多个更改sql,只有当你手动执行的时候才会刷新

当一次执行的sql过多时,用SimpleExecutor可能会有性能问题,此时你就可以选用BatchExecutor来执行sql

执行流程

默认情况下我们都是使用SimpleExecutor来执行sql,以doQuery为例看一下具体的执行流程

org.apache.ibatis.executor.SimpleExecutor#doQuery
在这里插入图片描述
这段代码就包含了整体的执行流程

  1. 首先根据配置创建一个StatementHandler
  2. 然后利用ParameterHandler给Statement设置参数
  3. 最后StatementHandler调用Statement执行sql,并通过ResultSetHandler将ResultSet封装成需要的对象,返回给用户

mybatis中有三种具体的StatementHandler,每个类的作用如下
请添加图片描述

StatementHandler 实现类作用
SimpleStatementHandler调用Statement执行sql,因此要执行的sql没有?,不用设置参数
PreparedStatementHandler调用PreparedStatement执行sql,sql有可能有?,当有?的时候需要设置参数
CallableStatementHandler调用CallableStatement执行脚本

ParameterHandler设置参数

org.apache.ibatis.executor.SimpleExecutor#prepareStatement
在这里插入图片描述
org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize
在这里插入图片描述
org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters
在这里插入图片描述
这个是设置参数最重要的方法,后续会单开一节来分析

另外你可以看到SimpleStatementHandler#parameterize方法是一个空实现,因为SimpleStatementHandler通过调用Statement来执行sql,不需要设置参数哈

org.apache.ibatis.executor.statement.SimpleStatementHandler#parameterize
在这里插入图片描述

StatementHandler调用Statement执行sql

调用BoundSql#getSql获取的sql只会有?,并且在前面通过ParameterHandler设置好参数了,因此直接执行就行,然后通过ResultSetHandler将返回的结果封装成特定的类型返回
在这里插入图片描述
ResultSetHandler我就不分析了哈,mybatis源码最复杂的一个类,没有之一

DefaultSqlSession有问题?

之前的文章我们分析过,在启动的过程我们会利用SqlSessionFactoryBuilder创建SqlSessionFactory,最终返回的是DefaultSqlSessionFactory,调用openSession方法返回的是DefaultSqlSession。
在这里插入图片描述

但是DefaultSqlSession这个api类还并不是很好用,有很多问题
在这里插入图片描述
比如DefaultSqlSession不是线程安全的,所以DefaultSqlSession不能是单例,另外我们还得手动关闭DefaultSqlSession

为什么DefaultSqlSession不是线程安全的呢?

因为一个DefaultSqlSession只会开启一个Connection。所以当多个线程使用DefaultSqlSession时,其中一个线程执行完毕,调用close方法,将Connection关闭,另一个线程就会报错
在这里插入图片描述
当使用sqlSessionManager时,会利用ThreadLocal来保证线程安全
在这里插入图片描述
getMapper方法传入的SqlSession是自己

org.apache.ibatis.session.SqlSessionManager#getMapper
在这里插入图片描述
因此当mapper接口操作sql时会调用到SqlSessionManager中的方法
在这里插入图片描述
SqlSessionManager将执行过程交给sqlSessionProxy,sqlSessionProxy又是一个动态代理类
在这里插入图片描述
sql的执行过程会进入SqlSessionInterceptor#invoke方法,先从ThreadLocal中获取,如果没有的话再创建,保证了线程安全
在这里插入图片描述
所以使用SqlSessionManager比直接使用DefaultSqlSession有如下2个好处

  1. 利用try-with-resource语法糖,解决自动关闭
  2. 利用ThreadLocal,解决线程安全

参考博客

sqlsession好文
[2]https://zhuanlan.zhihu.com/p/100533979

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java识堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值