zoukankan      html  css  js  c++  java
  • 【MyBatis系列5】MyBatis4大核心对象SqlSessionFactoryBuiler,SqlSessionFactory,SqlSession,Mapper

    前言

    前几篇篇我们简单讲解了MyBatis的简单用法,以及一对一和一对多以及多对多的相关动态sql查询标签的使用,也提到了嵌套查询引发了N+1问题,以及延迟加载相关功能,本篇文章将会从MyBatis底层实现着手,来分析MyBatis的4大核心对象。

    MyBatis四大核心对象

    我们先来回忆一下上一篇文章中,利用MyBatis来完成一次数据库操作需要经过哪些步骤,如下图所示:

    可以得出主要分为以下步骤:

    • 1、加载配置文件
    • 2、获取SqlSessionFactoryBuiler对象
    • 3、通过SqlSessionFactoryBuiler和配置文件流来获取SqlSessionFactory对象
    • 4、利用SqlSessionFactory对象来打开一个SqlSession
    • 5、通过SqlSession来获得对应的Mapper对象
    • 6、通过Mapper对象调用对应接口来查询数据库

    从这些步骤我们可以看到,MyBatic完成一次数据库操作主要有4大核心对象:SqlSessionFactoryBuiler,SqlSessionFactory,SqlSession和Mapper。

    SqlSessionFactoryBuiler

    SqlSessionFactoryBuilder的唯一作用就是用来创建SqlSessionFactory,创建完成之后就不会用到它了,所以SqlSessionFactoryBuiler生命周期极短。

    SqlSessionFactoryBuiler中提供了9个方法,返回的都是SqlSessionFactory对象。

     SqlSessionFactoryBuiler内使用到了建造者模式,如果想了解建造者模式的可以点击这里

    (2+1)方法分析

    虽然说提供了9个方法,但是实际上我们可以看成(2+1)个方法。

    请看下图,前面三个方法最终都调用了第4个方法,所以实际上就是一个方法

     在看下面这个其实也是一回事,有三个方法最终也是调用了同一个方法:

    所以这两个方法就对应了2+1方法中的2。
    上面图中也可以看到,这2个方法在将配置文件解析成一个Java配置类Configuration之后,又同时调用了另一个方法build(Configuration config),而这个方法也没做什么事,就是把配置类传进去返回了一个DefaultSqlSessionFactory对象,这个对象就是SqlSessionFactory的实现类

     所以说2+1中的1没什么好分析的,那么这个2其实就是支持了2种读取文件的方式,比如说我们获取一个SqlSessionFactory对象最常用的是通过如下方式:

    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    但是其实我们同样可以采用下面的方式才获取SqlSessionFactory对象:

    Reader reader = Resources.getResourceAsReader(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

    这两个方法的唯一区别就是在这里,另外两个参数都是一样的。

    • String environment:

    这个其实就是用来决定加载哪个环境的。
    下图就是我们一个mybatis文件,有定义了一个environment id,当我们需要支持多环数据源切换的时候可以定义多个id,然后根据不同环境传入不同id即可。

     Properties properties:上图中我们下面的dataSource内的属性没有直接输入,而是采用了${jdbc.driver}来配置,而这些属性的值我们可以单独维护在一个properties文件内。

     然后代码中就可以这么改写:

    Reader reader = Resources.getResourceAsReader(resource);
    Properties properties = Resources.getResourceAsProperties("db.properties");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,properties);

    采用这种方式之后,mybatis-config.xml文件可以直接用${}来取值了。当然其实还有一种更简单的方式,那就是直接在mybatis-config.xml引入properties文件,这样就不用在代码中去解析properties文件

    <properties resource="db.properties"></properties>

    build()方法的功能就是去读取XML文件,并将读取到的信息封装到Congiguration中,至于如何解析XML文件不是本文的重点,我们就不过多介绍了。

    通过上面的分析我们也很容易的知道,既然提供了一个方法可以直接传入Congiguration来获取SqlSessionFactory对象,那么其实我们也可以不采用XML形式,转而直接通过Java代码的方式来直接构建Congiguration,然后直接获取SqlSessionFactory对象。如下:

    Configuration congiguration = new Configuration();
    congiguration.setDatabaseId("xxx");
    congiguration.setCacheEnabled(false);
    SqlSessionFactory sqlSessionFactory1 = new SqlSessionFactoryBuilder().build(congiguration);

    当然,这种方式如果要修改的话每次都要改代码,会比较麻烦,而且看起来比较不直观,所以还是推荐使用xml方式来进行MyBatis的基础属性配置。

    InputStream和Reader

    Reader和InputStream都是Java中I/O库提供的两套读取文件方式。Reader用于读入16位字符,也就是Unicode 编码的字符;而 InputStream 用于读入ASCII字符和二进制数据。

    SqlSessionFactory

    SqlSessionFactory是一个接口,默认的实现类,主要是用来生成SqlSession对象,而SqlSession对象是需要不断被创建的,所以SqlSessionFactory是全局都存在的,也没有必要重复创建,所以这是一个单例对象。

    SqlSessionFactory看名字就可以很容易想到,用到的是工厂设计模式。

    SqlSessionFactory只有两个实现类:DefaultSqlSessionFactory和SqlSessionManager。

    DefaultSqlSessionFactory

    DefaultSqlSessionFactory是SqlSessionFactory默认的实现类,现在用的也基本上就是它。这个类提供了8个方法用来获取SqlSession对象。这个截图不太好截,为了方便对比,我把方法列举出来:

    SqlSession openSession();
    SqlSession openSession(boolean autoCommit);
    SqlSession openSession(TransactionIsolationLevel level);
    SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level);
    SqlSession openSession(ExecutorType execType);
    SqlSession openSession(ExecutorType execType, boolean autoCommit);
    SqlSession openSession(Connection connection);
    SqlSession openSession(ExecutorType execType, Connection connection);

    这8个方法中TransactionIsolationLevel和autoCommit都比较好理解,就是事务隔离级别和是否开启自动提交。
    前面6个方法最终实际上调用了openSessionFromDataSource方法,而后面2个又是调用了openSessionFromConnection方法。

     

    openSessionFromDataSource源码:
      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);
          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();
        }
      }
    openSessionFromConnection源码:
    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        try {
            boolean autoCommit;
            try {
                //获取事务是否自动提交
                autoCommit = connection.getAutoCommit();
            } catch (SQLException e) {
                // Failover to true, as most poor drivers
                // or databases won't support transactions
                autoCommit = true;
            }
            //获取配置环境
            final Environment environment = configuration.getEnvironment();
            //获取事务对象
            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            final Transaction tx = transactionFactory.newTransaction(connection);
            //获取执行器
            final Executor executor = configuration.newExecutor(tx, execType);
            //构造一个sqlSession对象返回
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }

    从上面的两个方法中很明显可以看到,这两个方法基本一样,唯一的区别是openSessionFromConnection方法是从Connection对象中获取当前会话是否需要开启自动提交。

    Executor

    Executor

    MyBatis中最终去执行sql语句是通过Executor对象实现的,Executor只有三种类型:
    SIMPLE, REUSE, BATCH。默认使用的是SIMPLE类型对应的SimpleExecutor执行器。SimpleExecutor表示普通的执行器,不会做任何处理。REUSE类型对应的是ReuseExecutor执行器,会复用预处理语句。BATCH类型对应的是BatchExecutor执行器,一般用来执行批量操作。
    Executor是属于SqlSession的四大对象之一,后面会有一篇博客来专门分析,在这里我们暂时不深入展开。

    SqlSessionManager

    SqlSessionManager其实也不是什么新东西,只是同时把SqlSessionFactory和SqlSession的职责放到了一起,所以这个我们不过多的讨论。

    SqlSession

    SqlSession是用来操作xml文件中我们写好的sql语句,每次操作数据库我们都需要一个SqlSession对象,SqlSession是用来和数据库中的事务进行对接的,所以SqlSession里面是包含了事务隔离级别等信息的。

    SqlSession实例是线程不安全的,故最佳的请求范围是请求(request)或者方法(method)。

    SqlSession也是一个接口,有两个实现类:DefaultSqlSession和SqlSessionManager。
    SqlSessionManager上面有提到,这里就不重复了,这里我们可以看一下这几个类的类图就会更清晰:

    Mapper

    Mapper是一个接口,没有任何实现类。主要作用就是用来映射Sql语句的接口,映射器的接口实例从SqlSession对象中获取,所以说Mapper实例作用域是和SqlSession相同或者更小。
    Mapper实例的作用范围最好是保持在方法范围,否则会难以管理。

    Mapper接口的名称要和对应sql语句的xml文件同名,Mapper接口中定义的方法名称对应了xml文件中的语句id。

    四大对象生命周期

    SqlSessionFactoryBuiler只需要在创建SqlSessionFactory对象的时候使用,创建完成之后即可被丢弃。

    SqlSessionFactory全局唯一,是一个单例对象,但是需要全局存在。

    SqlSession一般对应了一个request,

    Mapper一般控制在方法内。

    对象生命周期
    SqlSessionFactoryBuiler 方法局部(Method)使用完成即可被丢弃
    SqlSessionFactory 应用级别(Application),全局存在,是一个单例对象
    SqlSession 请求或方法(Request/Method)
    Mapper 方法(Method)

    总结

    本文主要分析了MyBatis中的四大核心对象:SqlSessionFactoryBuiler,SqlSessionFactory,SqlSession,Mapper。并从源码的角度分析了四大对象的生成过程以及生命周期。
    下一篇,将会分析SqlSession的执行流程,分析完之后就会了解到我们的参数以及结果集到底是如何实现映射的。


    ————————————————
    版权声明:本文为CSDN博主「双子孤狼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zwx900102/article/details/108581342

  • 相关阅读:
    VC 编译 MATLAB 的 mex 文件
    MATLAB 与 Excel 接口
    MATLAB 编译器的使用
    为什么安装了MinGW之后,还是不能在Matlab中使用mex?
    matlab文件操作
    matlab外部程序接口-excel
    数字图像加密-同态加密方案
    matlab数字图像简单的加密方法
    matlab中矩阵的表示与简单操作
    linux 安装eccodes环境
  • 原文地址:https://www.cnblogs.com/lzghyh/p/14672172.html
Copyright © 2011-2022 走看看