Mybatis源码解析二:DataSource数据源负责创建连接以及Transaction的事物管理

news2024/12/26 2:25:56

简介

对于一个成熟的ORM框架来说,数据源的管理以及事务的管理一定是不可或缺的组成,对于Mybatis来说,为了使用方便以及扩展简单也是做了一系列的封装,这一篇主要介绍mybatis是如何管理数据源以及事务的。

数据源DataSource

DataSource的分类

mybatis提供了三种数据源实现:
在这里插入图片描述

  • UNPOOLED:不使用连接池,每次请求都创建和关闭连接
  • POOLED: 使用连接池,避免每次请求创建何关闭连接,浪费资源
  • JNDI: 此实现旨在与EJB或应用程序服务器等容器一起使用,这些容器可以集中或外部配置DataSource,并在JNDI上下文中对其进行引用。(可以了解一下JNDI和JDBC的区别)

DataSource的创建过程

要创建哪种类型的DataSource是在mybatis的配置文件中决定的,指定对应的type类型:

<environments default="development">
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

MyBatis在初始化时,根据type属性来创建相应类型的的数据源DataSource,看一下解析dataSource的源码(在XMLConfigBuilder类中的environmentsElement方法中):

private void environmentsElement(XNode context) throws Exception {
  for (XNode child : context.getChildren()) {
    String id = child.getStringAttribute("id");
    if (isSpecifiedEnvironment(id)) {
      // Transaction标签的解析
      TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
      // DataSource标签的解析
      DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
      DataSource dataSource = dsFactory.getDataSource();
      Environment.Builder environmentBuilder = new Environment.Builder(id)
          .transactionFactory(txFactory)
          .dataSource(dataSource);
      configuration.setEnvironment(environmentBuilder.build());
    }
  }
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      // 获取type属性值
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      // 根据type创建DataSourceFactory
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
}

在上面的代码中,可以看到mybatis根据配置的type值创建对应的DataSourceFactory,再创建对应的DataSource,mybatis中一共有三种DataSourceFactory,正好对应数据源的类型:
在这里插入图片描述

创建DataSourceFactory的代码如下:

protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      // alias就是传进来的type的值
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
}
protected <T> Class<? extends T> resolveAlias(String alias) {
    // 在别名注册器中查找
    return typeAliasRegistry.resolveAlias(alias);
}
public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
    
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      // 别名注册器中查找,存在则直接返回对应的类型
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        // 直接返回type的值
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
}

别名注册器在Configuration初始化的时候已经将数据源以及事务的别名初始化好了,如下:

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

}

到这里数据源的创建过程就已经分析完了,主要就是解析DatsSource标签,再创建DatsSourceFactory,最后再创建对应DataSource

DataSource在何时被使用

在使用mybatis的时候,需要先获取SqlSessionFactory,再获取SqlSession,通过SqlSession创建mapper接口来执行sql操作,实际上执行逻辑的是mybatis中的执行器Executor,数据源的使用也正是在Executor执行中用来创建连接的,分析一下这段逻辑:

// SqlSession的创建逻辑
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 根据事物工厂创建事物(需要数据源,事物隔离级别以及是否自动提交的参数)
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 创建Executor
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
// Executor执行的查询逻辑
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
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 根据StatementHandler创建Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

StatementHandler是mybatis中一个重要的组件,这里先不展开,再创建完StatementHandler之后,需要创建具体的Statement来处理sql语句,数据源的使用也正是在prepareStatement方法中:

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;
}
protected Connection getConnection(Log statementLog) throws SQLException {
    // 从数据源中获取连接
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      // 创建代理的Connection对象
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
}

在创建Connection连接时,是通过transaction对象来完成的,transaction对象中拥有初始化好的DataSource以及事物隔离级别或者事务是否提交的属性,到这里Connection对象就已经创建完成了,接下来看一下mybatis中的事物

事务Transaction

事务分类

MyBatis包含两种TransactionManager类型(即type="[JDBC|MANAGED]”)
在这里插入图片描述

  • JDBC: JDBC-此配置只是直接使用JDBC提交和回滚。它依赖于从dataSource检索的连接来管理事务的范围。默认情况下,它在关闭连接时启用自动提交,以便与某些驱动程序兼容。然而,对于一些驱动程序来说,启用自动提交不仅是不必要的,而且是一项昂贵的操作。因此,自3.5.10版本以来,可以通过将“skipSetAutoCommitOnClose”属性设置为true来跳过此步骤。


  • MANAGED
    这种配置几乎什么都不做。它永远不会提交或回滚连接。相反,它允许容器管理事务的整个生命周期(例如JEE应用程序服务器上下文)。

Transaction的创建逻辑与DataSource是类似的,就不再分析了

Transaction的使用

在Executor执行的时候,创建连接是通过Transaction来构造的,直看创建连接的代码:

protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
}

Transaction在创建连接的时候会指定事务的隔离级别以及是否自动提交

总结

  • 数据源创建通过数据源工厂指定,工厂类型是在mybatis配置文件中指定的;数据源获取连接是在Executor执行器获取Statement时调用的
  • 事务创建也是通过事务工厂指定,同样是在mybatis配置文件中指定的,Transaction在获取到连接后,再进行设置事物的隔离级别以及是否自定提交

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/148420.html

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

相关文章

【深度学习】李宏毅2021/2022春深度学习课程笔记 - Adversarial Attack(恶意攻击)

文章目录一、基本概念1.1 动机1.2 恶意攻击的例子1.3 如何攻击&#xff1f;二、White Box vs Black Box三、One Pixel Attack四、Universal Adversarial Attack五、Beyond Image六、Attack in the Physical World七、Adversarial Reprogramming八、Backdoor in Model九、防御9.…

TLS回调函数实现反调试

title: TLS回调函数实现反调试.md date: 2022-06-16 23:40:49.231 updated: 2022-06-16 23:41:11.924 url: /archives/tls回调函数实现反调试 categories: tags: 逆向 TLS回调函数实现反调试 TLS-线程局部存储 先于我们OEP执行 #include<stdlib.h> #include<time.…

使用红黑树封装map、set

map、set如何用红黑树封装 map、set应用&#xff1a;map是一个使用参数K、参数V的类模板&#xff0c;set是只使用参数K的类模板。因为map应用时&#xff0c;需要使用到KV&#xff0c;而set只是存单个值&#xff0c;K。红黑树类的存储 &#xff1a;map和set类中使用红黑树数据成…

Logback配置详解

简介&#xff1a; logback是java的日志开源组件&#xff0c;是log4j创始人写的&#xff0c;性能比log4j要好&#xff0c;目前主要分为3个模块&#xff1a; logback-core:核心代码模块logback-classic:log4j的一个改良版本&#xff0c;同时实现了slf4j的接口&#xff0c;这样你…

树莓派mjpg-streamer实现监控功能

树莓派实现监控功能&#xff0c;调用mjpg-streamer库来实现。mjpg-streamer是一个开源的摄像头媒体流&#xff0c;通过本地获取摄像头的数据&#xff0c;通过http通讯发送&#xff0c;可以通过浏览器访问树莓派的IP地址和端口号就能看到视频流。 实现步骤 1.git clone https:…

关于内核的概念理解

狭义的操作系统可以认为就是内核&#xff0c;比如Linux内核。广义的操作系统则包括内核和一系列应用软件&#xff0c;比如Linux内核编辑器vim编译器gcc命令行解释器&#xff08;shell&#xff09;等&#xff0c;通常称为GNU/Linux。 源代码https://github.com/torvalds/Linux …

Jenkins自动化部署SpringBoot项目(windows环境)

文章目录1、Jenkins介绍1.1、概念1.2、优势1.3、Jenkins目的2、环境准备3、Jenkins下载3.1、下载3.2、运行3.3、问题解决4、Jenkins配置4.1、用户配置4.2、系统配置4.3、全局工具配置-最重要5、新建项目7、测试8、错误解决1、Jenkins介绍 1.1、概念 Jenkins是一个开源软件项目…

自动化测试Seleniums~1

一.什么是自动化测试 1.自动化测试介绍 自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或者系统&#xff0c;预设条件包括正常和异常&#xff0c;最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。 将测试人员双手解放&#xff0c;将部分测…

黑马javaWeb Brand综合案例

01-综合案例-环境搭建 02-查询所有-后台&前台

leetcode83周赛

前言&#xff1a; 周赛两题选手,有点意思 830.较大分组的位置 思路&#xff1a;wa了三发&#xff0c;对边界了解的不够清楚 可以有一个小小的优化,时间复杂度O(n) // arr.add(start); //arr.add(i-1); //res.add(arr); res.add(Arrays.asList(start,i - 1));class Solution {pu…

MATLAB-mesh/ezmesh函数三维图形绘制

l ) mesh 函数生成由X、Y和Z指定的网线面&#xff0c;由C指定颜色的三维网格图。具体调用方法如下。mesh(Z):分别以矩阵Z的行、列下标作为x轴和y轴的自变量绘图。mesh(X , Y,Z):最常用的一般调用格式。mesh(X,Y ,Z,C):完整的调用格式&#xff0c;C用于指定图形的颜色&#xff0…

Ubuntu 20.4 美化桌面、美化引导界面、Mac 既视感

文章目录相关资源安装 gnome-tweaks安装浏览器插件方法一方法二&#xff08;推荐&#xff09;主题美化进行美化配置效果图美化前美化后美化 Dock扩展推荐引导美化安装主题修改配置相关资源 https://pan.baidu.com/s/1D7ZfzVKMmeZPAzuDDAVUbg提取码&#xff1a;ws3f 安装 gnom…

Java基础学习笔记(十)—— 包装类与泛型

包装类与泛型1 包装类1.1 基本类型包装类1.2 Integer类1.3 自动装箱 / 拆箱2 泛型2.1 泛型概述2.2 泛型的用法2.3 类型通配符1 包装类 1.1 基本类型包装类 基本类型包装类的作用 将基本数据类型封装成对象 的好处在于可以在对象中定义更多的功能方法操作该数据 public stat…

C库函数:stdlib.h

stdlib.h C 标准库 – <stdlib.h> | 菜鸟教程 (runoob.com) 该库主要涉及“字符串和其他类型数据的转换”、“内存空间的申请和释放”、“查找和排序”、随机数等功能函数。 7void *calloc(size_t nitems, size_t size) 分配所需的内存空间&#xff0c;并返回一个指向它…

大幅度减少零样本学习所需的人工标注

零样本旨在模仿人类的推理过程&#xff0c;利用可见类别的知识&#xff0c;对没有训练的样本不可见类别进行识别&#xff0c; 类别嵌入&#xff1a;Class embedding&#xff1a; 描述类别语义和视觉特征的向量&#xff0c;能够实现知识在类别间的转移&#xff0c;因而在零样本…

Web进阶:Day2 空间转换、动画

Web进阶&#xff1a;Day2 Date: January 4, 2023 Summary: 空间转换、动画 空间转换 **空间&#xff1a;**是从坐标轴角度定义的。 x 、y 和z三条坐标轴构成了一个立体空间&#xff0c;z轴位置与视线方向相同 空间转换也叫3D转换 属性&#xff1a;transform 语法&#xff1…

SolidWorks二次开发 API-获取当前语言与重命名文件

新的一年了&#xff0c;开始新的分享。 做SolidWorks二次开发的时候&#xff0c;难免会遇到多语言的问题。 针对不同语言的客户生成不同语言的菜单&#xff0c;所以我们要知道Solidworks的当前界面语言是什么。 这个就简单的说一下方法: GetCurrentLanguage 看结果&#xff1a;…

二、MySQL进阶教程

mysql高级 1&#xff0c;约束 上面表中可以看到表中数据存在一些问题&#xff1a; id 列一般是用标示数据的唯一性的&#xff0c;而上述表中的id为1的有三条数据&#xff0c;并且 马花疼 没有id进行标示 柳白 这条数据的age列的数据是3000&#xff0c;而人也不可能活到3000岁…

Curator实现分布式锁(可重入 不可重入 读写 联锁 信号量 栅栏 计数器)

文章目录前言代码实践1. 配置2. 可重入锁InterProcessMutex3. 不可重入锁InterProcessSemaphoreMutex4. 可重入读写锁InterProcessReadWriteLock5. 联锁InterProcessMultiLock6. 信号量InterProcessSemaphoreV27. 栅栏barrier8. 共享计数器8.1. SharedCount8.2. DistributedAto…

再学C语言28:输入和输出——重定向和文件

默认情况下&#xff0c;使用标准I/O包的C程序将标准输入作为其输入源&#xff0c;即标识为stdin的流 stdin流是作为向计算机中读取数据的常规方式而建立&#xff0c;可以是键盘、语音等不同输入设备 现代计算机还可以从文件中需求其输入&#xff0c;而不仅仅是传统的输入设备…