Mybatis源码解析(三)------SqlSession

news/2024/5/17 15:59:07

Mybatis源码解析(三)------SqlSession

  • 序言
  • SqlSession接口
  • SqlSession的实现类
  • DefaultSqlSession
    • Select
      • 获取Statement
      • 查询

序言

Mybatis里面的核心就是SqlSession这个接口,前面我们已经研究了Mybatis的配置过程和Mapper的注册过程,在本篇文章中我们就来研究SqlSession。

/*** The primary Java interface for working with MyBatis.* Through this interface you can execute commands, get mappers and manage transactions.** @author Clinton Begin*/

SqlSession接口

先来看看sqlSession的接口:

接口方法接口描述
<T> T selectOne(String statement);查询单行数据,无参
<T> T selectOne(String statement, Object parameter);带参数查询单行数据
<E> List<E> selectList(String statement);无参数查询集合
<E> List<E> selectList(String statement, Object parameter);带参数查询集合
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);带参数查询指定行内的数据
<K, V> Map<K, V> selectMap(String statement, String mapKey);selectMap 是一种特殊情况,它旨在根据结果对象中的一个属性将结果列表转换为 Map。 例如。 为 selectMap(“selectAuthors”,“id”) 返回 Map[Integer,Author]
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);带参数,同上
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);带指定行数,同上
<T> Cursor<T> selectCursor(String statement);Cursor和list一样,不过Cursor用Iterator来懒加载数据
<T> Cursor<T> selectCursor(String statement, Object parameter);带参数,同上
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);带指定行数,同上
void select(String statement, Object parameter, ResultHandler handler);用ResultHandler来处理查询结果,返回一条数据(方法注释上是一条,handler注释上是处理每条,我觉得返回结果也是处理多条)
void select(String statement, ResultHandler handler);不带参,同上
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);带行数限制,同上
int insert(String statement);执行插入操作
int insert(String statement, Object parameter);带参数插入
int update(String statement);更新操作
int update(String statement, Object parameter);带参数更新
int delete(String statement);删除操作
int delete(String statement, Object parameter);带参数删除
void commit();刷新批处理语句并提交数据库连接。 请注意,如果没有调用更新/删除/插入,则不会提交数据库连接。 强制提交调用commit(boolean)
void commit(boolean force);刷新批处理语句并提交数据库连接。
void rollback();丢弃挂起的批处理语句并回滚数据库连接。 请注意,如果没有调用更新/删除/插入,数据库连接将不会回滚。 强制回滚调用rollback(boolean)
void rollback(boolean force);丢弃挂起的批处理语句并回滚数据库连接。 请注意,如果没有调用更新/删除/插入,数据库连接将不会回滚。
List<BatchResult> flushStatements();刷新批处理语句。
void close();关闭session连接
void clearCache();清除本地缓存
Configuration getConfiguration();获取当前的配置
<T> T getMapper(Class<T> type);获取Mapper
Connection getConnection();获取连接
看起来SqlSession接口定义很简单也很全面了,我们需要的数据库操作不外乎就是CRUD了。接着往下看……

SqlSession的实现类

Mybatis对SqlSession的实现有三个:DefaultSqlSession、SqlSessionManager、SqlSessionTemplate。
在这里插入图片描述
接下来分别对三个实现类进行分析。

DefaultSqlSession

SqlSession的默认实现

/*** The default implementation for {@link SqlSession}.* Note that this class is not Thread-Safe.** @author Clinton Begin*/

分别从CRUD来看看这个实现类吧。

Select

select是业务系统中使用最多的操作,根据源码绘制下图,从图中就可以看出,查询工作最终是落到了private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) 上。
在这里插入图片描述

  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, handler);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

MappedStatement:在原文中也没有注释,我们用原始的jdbc的时候也会有一步操作,获取Statement。至于Statement翻译成中文是什么,还真不好直译,网上说的 Statement是Java 执行数据库操作的一个重要接口,这里的MappedStatement也就是这个意思吧。

这里Select的过程就是从configuration里面那到对应的statement然后交个executor进行查询。

获取Statement

取得过程也很简单,就是去一个Map里面通过statment字符串去取就好了:

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection").conflictMessageProducer((savedValue, targetValue) ->". please check " + savedValue.getResource() + " and " + targetValue.getResource());public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {if (validateIncompleteStatements) {buildAllStatements();}return mappedStatements.get(id);}

可是,MappedStatement是如何放到Map里面的呢?利用Idea看了下,是在Mapper注册的时候放进去的。关于Mapper的注册可以看下Mybatis源码解析(二)------Mapper注册,不过我也没太仔细分析注册过程。
在这里插入图片描述

查询

那到Statement后,执行器开始执行查询操作,执行查询前看下跟一下这里的执行器是哪个,这个要从SqlSession session = sqlSessionFactory.openSession();开始看,SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);从这里得知是DefaultSqlSessionFactory, 然后这样再那样,得到执行器是SimpleExecutor,而SimpleExecutor继承的是BaseExecutor,并且没有重写query方法。那么接下来就是看看BaseExecutor的query方法了。

  @Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}

先从ms中取出BoundSql,然后生成一个缓存key。

BoundSql是什么?在处理完动态内容后,从SqlSource中取出的一个实际的Sql字符串,这条sql可能带有占位符?,还有一个参数映射集合,以及一些参数描述性信息。
在这里插入图片描述
换句话说就是BoundSQL就是解析我们的Mapper生成的,每个接口方法和sql对应一个BoundSql。

然后继续查询:

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}

先从缓存中取,然后没取到就从数据库查。从数据库取的时候肯定得王缓存里面放。

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}

doQuery是一个抽象方法,有子类实现。

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;

看下SimpleExecutor的doQuery方法吧:

 @Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}}

先看下StatementHandler的:

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}
public class RoutingStatementHandler implements StatementHandler {private final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}………………
}

上面这个结构,我觉着是啥设计模式呢?对设计模式了解不是非常深入,说是代理模式也不像啊,把整个对象都放给调用者了。有了解设计模式的大佬指教下呢。

在Demo这个场景下,这边用的是PreparedStatementHandler;接着,用StatementHandler来准备一个Statement,看下准备过程准备了什么:

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);return stmt;}

http://www.mrgr.cn/p/63164846

相关文章

【数学建模】时间序列分析

文章目录 1. 条件2. 模型分类3. SPSS处理时间序列 1. 条件 1.使用于具有时间、数值两种要素 2.数据具有周期性可以使用时间序列分解 2. 模型分类 叠加模型【YTSCI】 序列的季节波动变化越来越大&#xff0c;反映变动之间的关系发生变化乘积序列【YTSC*I】 时间序列波动保持恒…

舌体分割的初步展示应用——依托Streamlit搭建demo

1 前言 去年在社区发布了有关中医舌象诊断的博文&#xff0c;其中舌象识别板块受到了极高的关注和关注。&#x1f60a;最近&#xff0c;我接触到了Python的Streamlit库&#xff0c;它可以帮助数据相关从业人员轻松搭建数据看板。本文将介绍如何使用Streamlit构建舌体分割的演示…

网络层IP协议的基本原理 数据链路层ARP协议 域名解析以及一些重要技术

目录 1 网络层IP协议协议头格式网段划分DHCPCIDR&#xff1a;基于子网掩码的划分方式特殊的IP号IP地址的数量限制私有IP地址和公网IP地址路由路由表 2 数据链路层 — 局域网的转发问题以太网认识以太网以太网帧格式局域网通信原理 MTUMTU对IP协议的影响MTU对UDP协议的影响MTU对…

脑电信号处理与特征提取——6.运用机器学习技术和脑电进行大脑解码(涂毅恒)

目录 六、运用机器学习技术和脑电进行大脑解码 6.1 前言 6.2 基于脑电数据的机器学习基础分析 6.3 基于脑电数据的机器学习进阶分析 6.4 代码解读 六、运用机器学习技术和脑电进行大脑解码 6.1 前言 6.2 基于脑电数据的机器学习基础分析 6.3 基于脑电数据的机器学习进阶分…

谷粒商城第七天-商品服务之分类管理下的分类的拖拽功能的实现

目录 一、总述 1.1 前端思路 1.2 后端思路 二、前端实现 2.1 判断是否能进行拖拽 2.2 收集受影响的节点&#xff0c;提交给服务器 三、后端实现 四、总结 一、总述 这个拖拽功能对于这种树形的列表&#xff0c;整体的搬迁是很方便的。但是其实现却并不是那么的简单。 …

如何在不使用脚本和插件的情况下手动删除 3Ds Max 中的病毒?

如何加快3D项目的渲染速度&#xff1f; 3D项目渲染慢、渲染卡顿、渲染崩溃&#xff0c;本地硬件配置不够&#xff0c;想要加速渲染&#xff0c;在不增加额外的硬件成本投入的情况下&#xff0c;最好的解决方式是使用渲云云渲染&#xff0c;在云端批量渲染&#xff0c;批量出结…

大厂HR经常会问到的Java线程池面试题

一、什么是线程池 线程池和数据库连接池非常类似&#xff0c;可以统一管理和维护线程&#xff0c;减少没有必要的开销。 二、为什么要使用线程池 因为在项目开发过程中频繁的开启线程或者停止线程&#xff0c;线程需要重新被CPU从就绪状态调度到运行状态&#xff0c;需要发生C…

爆肝整理,接口自动化测试-数据驱动框架封装(实战)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口自动化框架—…

SpringBoot环境标识设置及nacos匹配配置

本地环境标识设置 本地父类maven配置 可以看到相关的分类&#xff0c;设置环境标识主要需要用到profiles; <profiles><profile><id>dev</id><properties><!-- 环境标识&#xff0c;需要与配置文件的名称相对应 --><profiles.active&…

2023项目管理产品排行榜:优化企业项目管理的顶级选择

随着全球竞争加剧和商业环境的变化&#xff0c;企业对项目管理的需求越来越迫切。优秀的项目管理产品能够帮助企业提高工作效率、资源利用率和项目交付质量。 本文参考了不同的产品测评网站&#xff0c;在众多项目管理产品中&#xff0c;总结了以下几款备受好评的项目管理工具&…

stm32 mpu6050 cubemx DMP法读取角度

文章目录 前言一、相关文件二、cubemx配置三、代码变量初始化主循环 总结 前言 文件 记录使用dmp库来读取mpu6050的角度。 这是参考文件 参考1–主要参考 github参考 参考2 参考三 一、相关文件 相关文件在这里下载&#xff08;未填&#xff0c;不过可以在上面的git中下载&a…

js全端支持的深拷贝structuredClone

Jul 7, 2023 经过一年半的试用&#xff0c;structuredClone转正了&#xff0c;全端可以正式使用。 https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

牛客网Verilog刷题——VL48

牛客网Verilog刷题——VL48 题目答案 题目 在data_en为高期间&#xff0c;data_in将保持不变&#xff0c;data_en为高至少保持3个B时钟周期。表明&#xff0c;当data_en为高时&#xff0c;可将数据进行同步。本题中data_in端数据变化频率很低&#xff0c;相邻两个数据间的变化&…

【Git】git reset 版本回退 git rm

前言 在日常开发时&#xff0c;我们经常会需要撤销之前的一些修改内容或者回退到之前的某一个版本&#xff0c;这时候reset命令就派上用场了 git reset 用法1——所有文件回退到某个版本 1、使用git reflog查看要回退的commit对象 2、使用git reset [-- hard/soft /mixed] …

计算机网络网--应用层

目录 应用层概述1.进程通信2. 供应用程序使用的运输服务3.因特网提供的运输服务 一.网络应用模型1.1 C/S模型&#xff08;client/server&#xff0c;客户/服务器模型&#xff09;定义工作原理功能特点优势应用 1.2 P2P&#xff08;peer to peer&#xff09;模型 与 P2P文件分发…

认识主被动无人机遥感数据、预处理无人机遥感数据、定量估算农林植被关键性状、期刊论文插图精细制作与Appdesigner应用开发

目录 第一章、认识主被动无人机遥感数据 第二章、预处理无人机遥感数据 第三章、定量估算农林植被关键性状 第四章、期刊论文插图精细制作与Appdesigner应用开发 更多推荐 遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多维、多地等角度&#xff0c;获取大量的…

ubuntu20.04 安装 Qt5.15

目录 安装前工作 选择安装QT的哪个版本 安装时候选择哪些组件 安装Qt5.15 在线安装 我选择的组件 源码包安装 测试 安装前工作 ubuntu20.04.3安装Qt6.22操作步骤_ubuntu安装qt6_sonicss的博客-CSDN博客 # 安装g、gcc编译器 sudo apt-get install build-essential 安装l…

HTTP——二、简单的HTTP协议

本章将针对 HTTP 协议结构进行讲解&#xff0c;主要使用HTTP/1.1版本。学完这章&#xff0c;想必大家就能理解 HTTP 协议的基础了。 HTTP 一、HTTP协议用于客户端和服务器之间的通信二、通过请求和响应的交换达成通信三、HTTP是不保存状态的协议四、请求URI定位资源五、告知服…

spring-websocket在SpringBoot(包含SpringSecurity)项目中的导入

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f96d;本文内容&#xff1a;spring-websocket在SpringBoot(包含SpringSecurity…

途乐证券:沪指强势拉升涨0.63%,券商等板块走强,传媒板块活跃

31日早盘&#xff0c;两市股指全线走高&#xff0c;沪指一度涨超1%收复3300点&#xff0c;上证50指数盘中涨逾2%&#xff1b;随后涨幅有所收窄&#xff1b;两市成交额显着放大&#xff0c;北向资金净买入超90亿元。 到午间收盘&#xff0c;沪指涨0.63%报3296.58点&#xff0c;深…