Mybatis 源 码 初 解 析 (一)

2023-10-29

搁置了好久的mybatis源码攻略,今天来一丢丢小小的总结。
阅读源码吗,我觉得要从宏观到微观的一个顺序去进行。有必要的话甚至可以加上自己的一个图解流程,可以使思路更加清晰。

初想mybatis

  1. 首先mybatis是一个为了简化操作的一个框架
  2. 然后它的存在代替了jdbc连接 那么它里面一定包括着连接数据库的基本操作

关于jdbc的操作

package jdbc;

import java.sql.*;
/**
 * @Author:XK
 * @Date: Created in 17:48 2021/10/26
 * @Description:jdbc的简单实现
 **/
public class jdbcFirstDemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2用户信息和URL
        String url ="jdbc:mysql://localhost:3306/fisrt21?useUnicod=true&characterEncoding=utf8&useSSL=true";
        String username="root";
        String password = "2020";

        //3连接成功 获取到数据库对象,Connection
        Connection connection = DriverManager.getConnection(url,username,password);

        //4.执行sql对象statement 执行sql的对象
        Statement statement = connection.createStatement();

        // 5 . 执行sql对象之后返回的结果集 Resultset
        String sql ="select * from class";

        ResultSet resultSet= statement.executeQuery(sql);

        while (resultSet.next()){
            System.out.println(resultSet.getString("class_name"));
        }

        //6 释放连接
        resultSet.close();
        statement.close();
        connection.close();

    }
}

我认为有三个部分,数据源,sql语句,数据操作。

  • 这里的数据源就是包括url,username,password的一个配置。通过这个可以连接到数据库
  • sql语句 就是我手动写的一条sql语句
  • 数据操作 就是我connection数据库对象获得sql对象 statement 然后 通过statement 执行sql语句 得到一个结果集的过程。
    这是我觉得很基本的一个jdbc连接数据库的操作。

既然mybats框架代替jdbc连接,那么它也一定完成这三方面的任务,而且顺序也差不多,究竟对不对就要去源码中验证。那么我带着这问题,进入到源码的阅读

mabatis 源码初读

不知道别人是怎么进行一个源码的阅读,对我来说,我就是写好一个demo然后通过idea的debug过程一步一步去看它这个框架究竟是怎么样的一个流程
通过mybatis官方文档 mybatis官方文档
并且从GitHub拉下mybatis的源码。

package test1;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import javax.sound.sampled.Line;
import java.io.IOException;
import java.io.InputStream;


/**
 * @Author:XK
 * @Date: Created in 16:25 2022/4/3
 * @Description:
 **/
public class demo {
  public static void main(String[] args) throws IOException {
    String resource = "resources/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    sqlSession.getMapper(UsrMapper.class).selectAll();
  }
}

写下这么一个小demo 开始解析。
首先从第一行代码来说,将config.xml的配置转化为inputstream 而后执行一个sqlsessionFactory的方法的build
熟悉mybatis的都知道,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
这一步就是通过config.xml的解析去构造出一个SqlSessionFactory 的实例,而且它是唯一的。

build方法究竟build了什么 点进去我们看

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

显然有一个XMLConfigBuilder类,通过类名我能大概猜想出这个玩意是干嘛的,大概就是用来解析xml文件的一个工具类把,显然在build 之前进行了一个XMLConfigBuilder.parse()方法。点进去看

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

  private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

调用了parseConfiguration方法,解析配置 。
看下具体某些方法,我熟悉的就是environments mappers 等 ,点进environmentsElement

  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          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());
          break;
        }
      }
    }
  }

从这段代码可以看出来每个字段对应的都对应着一个类,比如environments 对应着一个environment的类

public final class Environment {
  private final String id;
  private final TransactionFactory transactionFactory;
  private final DataSource dataSource;
  }

这里面的字段正好对应着config.xml的配置

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/fisrt21?useUnicod=true&amp;characterEncoding=utf-8&amp;useSSL=true"/>
                <property name="username" value="root"/>
                <property name="password" value="2020"/>
            </dataSource>
        </environment>
    </environments>

可以看到最后将值拿到之后都会进行一个 configuration.setXXXX ,那么这个configuration究竟是什么

  protected Environment environment;

  protected boolean safeRowBoundsEnabled;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase;
  protected boolean aggressiveLazyLoading;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys;
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls;
  protected boolean useActualParamName = true;
  protected boolean returnInstanceForEmptyRow;
  protected boolean shrinkWhitespacesInSql;
  protected boolean nullableOnForEach;

发现它包含着这么一系列的配置参数吧属于是,所以猜想他整个mybatis的一些个配置层。
这个过程他把xml文件里的配置全部读取到configuration这个类的实例里面
结束之后 执行build

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

可以看到返回一个DefaultSqlSessionFactory默认的SqlSessionFactory,里面有selectone或者是getmapper的方法,从这看出来这个mybatis应用的核心SqlSessionFactory已经被创建出一个实例了。验证了我的猜想。
首先config.xml 里的environment就是一个url,以及username,password 的配置。
所以这一步可以说创造了数据源。接下来就是寻找sql语句是如何获取的。

Mybatis 源 码 初 解 析 (二)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mybatis 源 码 初 解 析 (一) 的相关文章

  • 如何将本机库链接到 IntelliJ 中的 jar?

    我正在尝试在 IntelliJ 中设置 OpenCV 但是我一直在弄清楚如何告诉 IntelliJ 在哪里可以找到本机库位置 在 Eclipse 中 添加 jar 后 您可以在 Build Config 屏幕中设置 Native 库的位置
  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • Mockito:如何通过模拟测试我的服务?

    我是模拟测试新手 我想测试我的服务方法CorrectionService correctPerson Long personId 实现尚未编写 但这就是它将执行的操作 CorrectionService将调用一个方法AddressDAO这将
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • java.lang.IllegalStateException:应用程序 PagerAdapter 更改了适配器的内容,而没有调用 PagerAdapter#notifyDataSetChanged android

    我正在尝试使用静态类将值传递给视图 而不是使用意图 因为我必须传递大量数据 有时我会收到此错误 但无法找出主要原因是什么 Error java lang IllegalStateException The application s Pag
  • Java 集合的并集或交集

    建立并集或交集的最简单方法是什么Set在 Java 中 我见过这个简单问题的一些奇怪的解决方案 例如手动迭代这两个集合 最简单的单行解决方案是这样的 set1 addAll set2 Union set1 retainAll set2 In
  • java.lang.IllegalStateException:提交响应后无法调用 sendRedirect()

    这两天我一直在尝试找出问题所在 我在这里读到我应该在代码中添加一个返回 我做到了 但我仍然得到 java lang IllegalStateException Cannot call sendRedirect after the respo
  • 在 junit 测试中获取 javax.lang.model.element.Element 类

    我想测试我的实用程序类 ElementUtils 但我不知道如何将类作为元素获取 在 AnnotationProcessors 中 我使用以下代码获取元素 Set
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • 获取文件的总大小(以字节为单位)[重复]

    这个问题在这里已经有答案了 可能的重复 java 高效获取文件大小 https stackoverflow com questions 116574 java get file size efficiently 我有一个名为 filenam
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐