mybatis的原理详解

news2024/12/24 0:24:34

mybatis的原理详解

原理图

执行的原理图如下图所示:

在这里插入图片描述

配置文件分析

config.xml:

<?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>
<!--    配置 mybatis的环境-->
    <environments default="development">
<!--        配置环境-->
        <environment id="development">
<!--            配置事物类型-->
            <transactionManager type="JDBC"></transactionManager>
<!--            配置连接数据库的信息:用的是数据源[连接池]-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--                jdbc:mysql://localhost:3306/db_school?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC-->
<!--                和javaWeb servlet三层架构中的区别这里是只需要设置时区就可以了-->
                <property name="url" value="jdbc:mysql://localhost:3306/db_school?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="h123456"/>
            </dataSource>
        </environment>
    </environments>
<!--    注册StudentDao接口映射文件位置-->
    <mappers>
        <mapper resource="mapper/StudentMapper.xml"/>
    </mappers>
</configuration>

核心配置文件参数详解:

environment标签中的id属性必须和enviroments标签中的default属性一致。

事务管理器:

第一种采用JDBC事务类型,直接使用了JDBC的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

第二种采用MANAGEd事务类型,它从提交或者回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将closeConnection属性设置为false来阻止默认的关闭行为。例如:

< transactionManager type=“MANAGED”>

​ < property name=“closeConnection” value=“false”/>

</ transactionManager>

数据源(dataSource)

dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源。大多数 MyBatis应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type =“[UNPOOLED|POOLED|JNDI]”)

UNPOOLED- 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据连接可用性要求不高的简单应用程序来说,是一个很好的选择。性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。

POOLED- 这种数源的实现利用"池"的概念将JDBC连接对象组织起来,避免了创建新建的连接实列时所必须的初始化和认证时间。这种处理方式很流行,能使并发web应用快速响应请求。

JNDI - 这个数据源实现是为了能在入EJB 或者应用服务器这类容器中使用的,容器可以集中或外部配置数据源,然而放置一个JNDI上下文的数据源引用。

mapper 中的resource属性用于指定映射文件的位置。mapper中有多个属性可以描述映射文件,分别为:

resource=“mapper/StudentMapper.xml"文件在多级目录中要用分隔符”/"隔开。

class=“com.etime.dao.StudentDao” 指定StudentDao接口文件位置,但此时StudentDao.xml必须和接口处在同一个包中并且文件名要相同。

映射文件:

StudentMapper.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace是当前mapper对应的dao接口-->
<mapper namespace="com.etime.dao.StudentDao">
<!--    select指定当前进行数据库操作的是查询-->
<!--    id的值对应的是当前dao层接口中的方法名字-->
<!--    resultType指定当前查询得到的数据要封装成的类型-->
    <select id="getAllStudents" resultType="com.etime.pojo.Student">
        select  * from student
    </select>
</mapper>

映射文件参数详解

namespacce必须为接口的完全限定名(即包名+类名的格式)

select标签中的id必须和接口中声明的方法同名

如果接口中方法有返回值,resultType必须跟方法返回值一致并采用返回值的完全限定名来表示。

底层源码分析

在这里插入图片描述

  • 利用Resources的getResourceAsStream方法读取mybatis核心配置文件,该配置文件中注册数据源[dataSource]和映射的文件的位置[mappers标签中的mapper子标签的resource属性]
inputStream in = Resources.getResourceAsStream("config.xml");
  • 展开映射文件StudentMapper.xml中定义了子查询方法、查询时所封装 结果类型和所要执行的sql语句。
< select id = "getAllStudents" resultType="com.etime.pojo.Student">

​		select * from student

</ select>
  • 使用构建者模式SqlSessionFactoryBuilder类的builder方法创建SqlSessionFactory对象,在build方法中从字节输入流中解析数据
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(in);
SqlSessionFactoryBuilder.java的build方法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  SqlSessionFactory var5;
  try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      var5 = this.build(parser.parse());
  } catch (Exception var14) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
  } finally {
      ErrorContext.instance().reset();

      try {
          inputStream.close();
      } catch (IOException var13) {
      }

  }

  return var5;
}
XMLConfigBuilder的parse方法:
public Configuration parse() {
  if (this.parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  } else {
      this.parsed = true;
      this.parseConfiguration(this.parser.evalNode("/configuration"));
      return this.configuration;
  }
}    

XMLConfigBuilder的parseConfiguration方法:
 private void parseConfiguration(XNode root) {
  try {
      this.propertiesElement(root.evalNode("properties"));
      Properties settings = this.settingsAsProperties(root.evalNode("settings"));
      this.loadCustomVfs(settings);
      this.loadCustomLogImpl(settings);
      this.typeAliasesElement(root.evalNode("typeAliases"));
      this.pluginElement(root.evalNode("plugins"));
      this.objectFactoryElement(root.evalNode("objectFactory"));
      this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
      this.settingsElement(settings);
      this.environmentsElement(root.evalNode("environments"));
      this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      this.typeHandlerElement(root.evalNode("typeHandlers"));
      this.mapperElement(root.evalNode("mappers"));
  } catch (Exception var3) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
  }
} 

XMLConfigBuilder的mapperElement方法:    
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
      Iterator var2 = parent.getChildren().iterator();

      while(true) {
          while(var2.hasNext()) {
              XNode child = (XNode)var2.next();
              String resource;
              if ("package".equals(child.getName())) {
                  resource = child.getStringAttribute("name");
                  this.configuration.addMappers(resource);
              } else {
                  resource = child.getStringAttribute("resource");
                  String url = child.getStringAttribute("url");
                  String mapperClass = child.getStringAttribute("class");
                  XMLMapperBuilder mapperParser;
                  InputStream inputStream;
                  if (resource != null && url == null && mapperClass == null) {
                      ErrorContext.instance().resource(resource);
                      inputStream = Resources.getResourceAsStream(resource);
                      mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                      mapperParser.parse();
                  } else if (resource == null && url != null && mapperClass == null) {
                      ErrorContext.instance().resource(url);
                      inputStream = Resources.getUrlAsStream(url);
                      mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                      mapperParser.parse();
                  } else {
                      if (resource != null || url != null || mapperClass == null) {
                          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                      }

                      Class<?> mapperInterface = Resources.classForName(mapperClass);
                      this.configuration.addMapper(mapperInterface);
                  }
              }
          }

          return;
      }    

上述代码中我们可以看到创建了一个XMLConfigBuilder对象用来解析XML,把配置文件中所有标签及标签中属性值放封装Configuration对象中并返回SqlSessionFactory对象。

  • 调用SqlSessionFactory对象的openSession方法返回一个SqlSession对象,SqlSessionFactory是一个接口,我们找到它的实现类DefaultSqlSessionFactory类,点击它的openSession方法,打开openSessionFromDataSource方法。
SqlSession sqlSession = sqlSessionFactory.openSession();

DefaultSqlSessionFactory.java的openSessionFromDataSource方法:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;

  DefaultSqlSession var8;
  try {
      Environment environment = this.configuration.getEnvironment();
      TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
      //根据环境的配置创建一个新的事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      Executor executor = this.configuration.newExecutor(tx, execType);
      //创建DefaultSqlSession对象,即SqlSession对象
      var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
  } catch (Exception var12) {
      this.closeTransaction(tx);
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
  } finally {
      ErrorContext.instance().reset();
  }

  return var8;
}

  • 调用sqlSession.getMapper(StudentDao.class)方法返回一个StudentDao接口的代理类对象。
DefaultSqlSessionFactory.java的getMapper方法: 
public <T> T getMapper(Class<T> type) {
  return this.configuration.getMapper(type, this);
}

Configuration.java的getMapper方法:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return this.mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry.java的getMapper方法:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
  if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  } else {
      try {
          return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception var5) {
          throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
      }
  }
}

mapperProxyFactory.java的newInstance方法:            
public T newInstance(SqlSession sqlSession) {
  MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
  return this.newInstance(mapperProxy);
}

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

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

相关文章

PTA L1-091 程序员买包子 (10 分)

这是一条检测真正程序员的段子&#xff1a;假如你被家人要求下班顺路买十只包子&#xff0c;如果看到卖西瓜的&#xff0c;买一只。那么你会在什么情况下只买一只包子回家&#xff1f; 本题要求你考虑这个段子的通用版&#xff1a;假如你被要求下班顺路买 N N N 只包子&#x…

华为云上云实践(二):Linux 环境下对云硬盘 EVS 的创建、挂载和初始化

本文主要讲解华为云云硬盘 EVS 的在 Linux 操作系统 EC2 服务器上创建、挂载及云硬盘初始化等基本操作&#xff0c;快速掌握华为云云硬盘 EVS 操作方法。 How to attach new Huawei EVS Volume 文章目录 一、前言二、环境准备与造作步骤2.1 本文实践操作的环境2.2 本文实践操作…

基础数据结构------单链表

1、链表使用的解决方案 【链表的概述】 链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点&#xff08;链表中每一个元素称为结点&#xff09;组成&#xff0c;结点可以在运行时动态生成。…

从MySQL小表驱动大表说起

刚刚开始学习MySQL的时候&#xff0c;连表查询直接使用left join或者更粗暴点&#xff0c;两个表直接查询&#xff0c;where过滤条件&#xff0c;组装笛卡尔积&#xff0c;最终出现自己想要的结果。 当然&#xff0c;现在left join也是会用的&#xff0c;毕竟嘛&#xff0c;方便…

如何实现多存储文件传输,镭速提供多存储文件传输解决方案

目前的文件传输系统中&#xff0c;大多数采用的文件传输系统只支持单个的存储。随着科技的发展&#xff0c;存储的类型越来越多&#xff0c;构建的越来越复杂&#xff0c;业务要求越来越多样化&#xff0c;只支持单个存储的文件传输系统是无法满足现有的需求。 为实现高自由度…

Java基础(十九):集合框架

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 Java基础(四)&#xff1a;逻辑运算符和位运算符 Java基础(五)&#xff1a;流程控制语句 Java基础(六)&#xff1…

vue3新的组件

1.Fragment - 在Vue2中: 组件必须有一个根标签 - 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中 - 好处: 减少标签层级, 减小内存占用 没有写根标签&#xff0c;也没有报错&#xff0c;如果是在v2中&#xff0c;我们还需要用一个div来包裹它 …

springboot web项目统一时区方案

背景 springboot项目国际化中&#xff0c;会遇到用户选择的时间和最终存到数据库的时间不一致&#xff0c;可能就是项目开发和部署时的时区没有处理好&#xff0c;导致时间转换出现了问题。 先了解时区都有哪些&#xff1a; 1.GMT&#xff1a;Greenwich Mean Time 格林威治…

移动端适配方法:rem+vw

1.百分比设置:几乎不用 因为各种属性百分比参照物(自身/父元素/...需要去查文档)很难统计固定,所以不用百分比进行适配 2.rem单位动态html的font-size 使用rem,因为rem参考html的fz,只需要在不同的屏幕上设置不同的html的fz即可,其他地方全用rem rem的fz尺寸 媒体查询 编写…

推荐系统召回之userCF

基于用户的协同过滤算法userCF 1.1 相似度计算 通过计算用户之间的相似度。这里的相似度指的是两个用户的兴趣相似度。 假设对于用户u uu和v vv&#xff0c;N ( u ) N(u)N(u)指的是用户u uu喜欢的物品集合&#xff0c;N ( v ) N(v)N(v)指的是用户v vv喜欢的物品集合&#xff0…

体验 Kubernetes Cluster API

体验 Kubernetes Cluster API 什么是 Kubernetes Cluster API安装 Kind增加 ulimit 和 inotify创建 Kind 集群安装 clusterctl CLI 工具初始化管理集群创建一个工作负载集群访问工作负载集群部署一个 CNI 解决方案安装 MetalLB部署 nginx 示例清理(参考)capi-quickstart.yaml 文…

C++的类和对象(2)

类和对象 1.类对象模型1.1. 如何计算类对象的大小1.2. 类的存储模式讨论1.3. 类对象的空间符合结构体对齐规则 2. this指针2.1. this指针的引出2.2. this指针的特性2.3.面试题2.4. C语言和C实现栈的对比 1.类对象模型 1.1. 如何计算类对象的大小 class A { public: void Prin…

类加载与卸载

加载过程 其中验证,准备,解析合称链接 加载通过类的完全限定名,查找此类字节码文件,利用字节码文件创建Class对象. 验证确保Class文件符合当前虚拟机的要求,不会危害到虚拟机自身安全. 准备进行内存分配,为static修饰的类变量分配内存,并设置初始值(0或null).不包含final修饰…

用python脚本从Cadence导出xdc约束文件

用python脚本从Cadence导出xdc约束文件 概述转换方法先导出csv文件修改CSV文件 CSV转XDC检查输出XDC文件csv2xdc源代码下载 概述 在Cadence设计完成带有FPGA芯片的原理图的时候&#xff0c;往往需要将FPGA管脚和网络对应关系导入vivado设计软件中&#xff0c;对于大规模FPGA管…

springboot+vue准妈妈孕期交流平台(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的准妈妈孕期交流平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;…

C++引用篇

文章目录 一、引用概念及示例二、引用做函数参数二、引用做函数的返回值四、常引用五、引用和指针的区别 一、引用概念及示例 c语言指针存变量地址&#xff0c;然后通过解引用可以访问或者改变变量&#xff0c;且也可以改变指针变量里面存的地址 修改变量这样还需要对指针变量…

Faster RCNN系列3——RPN的真值详解与损失值计算

Faster RCNN系列&#xff1a; Faster RCNN系列1——Anchor生成过程 Faster RCNN系列2——RPN的真值与预测值概述 Faster RCNN系列3——RPN的真值详解与损失值计算 Faster RCNN系列4——生成Proposal与RoI Faster RCNN系列5——RoI Pooling与全连接层 目录 一、RPN真值详解二、…

手把手教你实现el-table实现跨表格禁用选项,以及禁用选择后,对应的全选按钮也要禁用任何操作

哈喽 大家好啊 今天我要实现不能跨表格选择&#xff0c;如果我选择了其中一个表格的选项后&#xff0c;那么其他的表格选项则被禁用 然后我选择了其中一个表格行&#xff0c;我其他的表格选项则应该被禁用 实现代码&#xff1a; 其中关键属性&#xff1a; selectable仅对 typ…

如何保障企业网络安全

随着信息技术的迅速发展&#xff0c;网络已经渗透到了我们生活的方方面面。企业对网络的依赖程度也越来越高&#xff0c;网络安全问题已经成为了企业面临的一个重要挑战。那么&#xff0c;在这个风险重重的网络世界里&#xff0c;我们如何充分利用现有技术保障企业网络安全呢&a…

智能指针——C++

智能指针相较于普通指针的区别&#xff0c;就是智能指针可以不用主动释放内存空间&#xff0c;系统会自动释放&#xff0c;避免了内存泄漏。 1、unique_ptr&#xff1a;独占指针 需包含的头文件&#xff1a;#include <memory> unique_ptr 三种定义方式 先定义一个类 …