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

news2024/10/5 13:02:00

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方法了。

  @Override
  public <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。

然后继续查询:

@Override
  public <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 #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    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方法吧:

 @Override
  public <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.coloradmin.cn/o/816297.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jsp实现打印功能

1.先实现列表页查询 2.做一个打印按钮 function plprint(){var rows $(#whYcfTzList).datagrid(getData);var ORGCODE$(input[nameORGCODE]).val();var ISCONTAIN$(input[nameISCONTAIN]).val();var RECCODE$(input[nameRECCODE]).val();var CUSTOMERNAME$(input[nameCUSTOM…

安卓抓包神器黄鸟HttpCanary安装配置及使用教程

1、下载安装包 黄鸟抓包下载地址 2、安装下载的apk 3、证书安装问题 vivo手机我安装时打开黄鸟app&#xff0c;会直接弹出&#xff0c;直接安装即可 其他手机&#xff0c;需要去系统设置中安装 3.1 搜索 证书&#xff0c;选择CA证书 3.2 进行本人操作验证 3.3 安装HttpCa…

【LeetCode】下降路径最小和

下降路径最小和 题目描述算法分析编程代码 链接: 下降路径最小和 题目描述 算法分析 编程代码 class Solution { public:int minFallingPathSum(vector<vector<int>>& matrix) {int n matrix.size();vector<vector<int>> dp(n1,vector(n2,INT_M…

【密码学】五、序列密码

序列密码 1、概述1.1序列密码的分类1.1.1同步序列密码1.1.2自同步序列密码 2、序列密码的组成2.1密钥序列生成器KG2.2有限状态自动机 3、LFSR 1、概述 采用一个短的种子密钥来控制某种算法获得长的密钥序列的办法&#xff0c;用以提供加解密&#xff0c;这个种子密钥的长度较短…

java实现文件下载

1.文件上传 文件上传&#xff0c;也称为upload&#xff0c;是指将本地图片、视频、音频等文件上传到服务器上&#xff0c;可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛&#xff0c;我们经常发微博、发微信朋友圈都用到了文件上传功能。 import com.itheima.…

Leetcode-每日一题【剑指 Offer II 006. 排序数组中两个数字之和】

题目 给定一个已按照 升序排列 的整数数组 numbers &#xff0c;请你从数组中找出两个数满足相加之和等于目标数 target 。 函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 0 开始计数 &#xff0c;所以答案数组应当满足 0 < answer[0] &l…

简述token和如何使用token

一、什么是token&#xff08;理论&#xff09; 解决http短连接,无状态管理的问题。 Jeb web token(JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开发标准&#xff0c;JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息&#xff0c;以便于…

ChatGPT在法律行业的市场潜力

​ChatGPT现在已经成为我们的文字生成辅助工具、搜索引擎助手&#xff0c;许多体验过它的朋友会发现对它越来越依赖&#xff0c;并将其逐渐融入到自己的日常工作、生活。但有一点值得注意&#xff1a;这种人工智能除了技术可行、经济价值可行还要与相关规范即人类普遍的价值观念…

JVM复习(史上最全!!!)

一、JDK、JRE、JVM的区别 JDK: 全称Java Development Kit&#xff0c;是 Java 语言的软件开发工具包&#xff0c;主要用于移动设备、嵌入式设备上的Java应用程序。JDK是整个Java开发的核心。 JRE: JRE&#xff0c;全称Java Runtime Environment&#xff0c;是指Java的运行环境&…

vue3如何封装框架

在Vue 3中&#xff0c;你可以通过创建一个基础的框架来封装一些常用的功能、组件和样式&#xff0c;以便在不同的项目中重复使用。下面是一个简单的步骤来封装一个Vue 3框架&#xff1a; 创建一个新的Vue项目&#xff1a;首先&#xff0c;使用Vue CLI创建一个新的Vue项目。 v…

eam资产管理系统,eam资产管理系统功能介绍

基于固定资产管理系统PDA并结合RFID技术固定资产管理信息系统将固定资产管理、低价值消耗品管理和设备维护融为一体。根据先进的射频识别技术&#xff0c;从资产购买公司到资产退出的整个生命周期&#xff0c;每一个固定资产唯一的条形码资产标签都被赋予了后续管理&#xff0c…

【多模态】23、RO-ViT | 基于 Transformer 的开发词汇目标检测(CVPR2023)

文章目录 一、背景二、方法2.1 基础内容2.2 Region-aware Image-text Pretraining2.3 Open-vocabulary Detector Finetuning 三、效果3.1 细节3.2 开放词汇目标检测效果3.3 Image-text retrieval3.4 Transfer object detection3.5 消融实验 论文&#xff1a;Region-Aware Pretr…

大数据课程D4——hadoop的YARN

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解YARN的概念和结构&#xff1b; ⚪ 掌握YARN的资源调度流程&#xff1b; ⚪ 了解Hadoop支持的资源调度器&#xff1a;FIFO、Capacity、Fair&#xff1b; ⚪ 掌握YA…

4090Ti被取消,NVIDIA还要推出新“甜品卡“

不知不觉距离 NVIDIA RTX 40 系显卡发布已快一年&#xff0c;4090 到 4060 从旗舰到甜品也都差不多了。 不过每个男孩子都想要的礼物 - RTX 4090 Ti &#xff0c;至今仅在春晚发布。 从核心架构上来看&#xff0c;RTX 4090 上的 AD 102-300 也确实不是完全体。 仅拥有144组 S…

模拟一个一维排斥场

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让A有6个1&#xff0c;B有4个1&#xff0c;比较迭代次数的顺序。 其中有12组数据 差值结构 迭代次数 全0行 位置 构造平均列 平均列 列排斥能 …

手机照片转换成pdf怎么做?了解这几种方法就可以了

手机照片转换成pdf怎么做&#xff1f;转换照片为PDF的需求在日常生活中很常见。无论是收集有关旅行、家庭或工作的照片&#xff0c;将它们组织成一个PDF文件可以更方便地分享给朋友或同事。那么下面就给大家分享几个手机照片转换成pdf的方法。 虽然有多种软件和工具可以将照片转…

八大排序算法--直接插入排序(动图理解)

目录 直接插入排序 概念 算法思路 动画演示 代码如下 复杂度分析 时间复杂度测试 运行结果 完整代码 创作不易&#xff0c;如果本篇博客对您有一定的帮助&#xff0c;大家记得留言点赞哦。 直接插入排序 概念 直接插入排序是插入排序的一种。把待排序的数字按大小逐个插…

【SpringBoot】笔记2

文章目录 45、web实验-抽取公共页面46、web实验-遍历数据与页面bug修改47、视图解析-【源码分析】-视图解析器与视图[暂时没看]48、拦截器-登录检查与静态资源放行49、拦截器-【源码分析】-拦截器的执行时机和原理50、文件上传-单文件与多文件上传的使用51、文件上传-【源码流程…

jQuery如何获取动态添加的元素

jQuery如何获取动态添加的元素 使用 on()方法 本质上使用了事件委派&#xff0c;将事件委派在父元素身上 自 jQuery 版本 1.7 起&#xff0c;on() 方法是 bind()、live() 和 delegate() 方法的新的替代品&#xff0c;但是由于on()方法必须有事件&#xff0c;没有事件时可选择de…

elasticsearch 官方优化建议

.一般建议 a.不要返回过大的结果集。这个建议对一般数据库都是适用的&#xff0c;如果要获取大量结果&#xff0c;可以使用search_after api&#xff0c;或者scroll &#xff08;新版本中已经不推荐&#xff09;。 b.避免大的文档。 2. 如何提高索引速度 a.使用批量请求。为了…