mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建

原创 2017年01月03日 20:47:36

mybatis源码学习及分析之执行过程分析——SqlSessionFactory及SqlSession的创建

说在前面:首先来看一段JDBC获取数据的代码。

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/mybatis_study";
        Connection conn = DriverManager.getConnection(url, "root", "123456");
        PreparedStatement statement = null;
        ResultSet rs = null;
        if (conn != null) {
            System.out.println("get conn");
            String sql = "select id,name from user where id=?";
            statement = conn.prepareStatement(sql);
            statement.setInt(1, 1);
            statement.execute();

            rs = statement.getResultSet();
            if (rs != null) {
                System.out.println("get rs");
                User user = new User();
                for (; rs.next(); ) {
                    user.setId(rs.getInt("id"));
                    user.setName(rs.getString("name"));
                    //...
                }
                System.out.println(JSON.toJSONString(user));
            }
        }
        rs.close();
        conn.close();
        statement.close();
    }

在使用JDBC时,我们所面临的烦恼就是不断的获取Connection、拼接sql、和转换ResultSet。试想一个User有100个字段,那我们的代码就没有可读性了,犯错的概率极大。
mybatis作为一个ORM框架就是为了帮助我们解决这些问题的,包括Connection获取、sql拼接、ResultSet转换为POJO几个方面。接下来的系列文章,会看到mybatis如何处理以上问题的。

分析采用mybati-3.4.1.jar
mybatis的工程结构如图:
这里写图片描述

与执行流程相关的类主要有:

    SqlSessionFactory
    SqlSessionFactoryBuilder
    DefaultSqlSessionFactory
    Configuration

    DefaultSqlSession

    MapperProxyFactory
    MapperProxy
    MapperMethod

    CachingExecutor
    BaseExecutor
    SimpleExecutor
    RoutingStatementHandler

    PreparedStatementHandler

    ResultMap
    MappedStatement

下面来看我们程序中的调用流程:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

首先会创建SqlSessionFactory 。在SqlSessionFactoryBuilder调用build(Reader reader) 来创建SqlSessionFactory,并将我们的配置文件(mybatis-config.xml)流包装为Reader传入。

SqlSessionFactoryBuilder.java

    public SqlSessionFactory build(Reader reader) {
        return this.build((Reader)reader, (String)null, (Properties)null);
    }

build(Reader reader) 调用了build(Reader reader, String environment, Properties properties),该方法执行XML文件的解析,并调用build(Configuration config) 创建出默认的SqlSessionFactory 对象。

SqlSessionFactoryBuilder.java

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            //解析Mybatis-config.xml配置文件中的内容,包括xml文件的校验,以及从<configuration>标签解析mybatis的各项配置,并返回Configuration对象。解析的过程中对Configuration的许多默认参数做了设置,详情见XMLConfigBuilder类的分析。
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);       
            var5 = this.build(parser.parse());  //parser.parse()返回Configuration实例。
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();  //TODO 异常类的分析

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

        }

        return var5;
    }

    //将Configuration实例传给DefaultSqlSessionFactory
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);        //默认创建的SqlSessionFactory为DefaultSqlSessionFactory
    }

SqlSessionFactory顾名思义就是用来提供各种方法来获取SqlSession。或配置Configuration。

这里写图片描述

这里写图片描述

这里主要用到DefaultSqlSessionFactory,其实现了SqlSessionFactory中的openSession等方法,提供了从数据源和连接中获取Session的方法,并提供获取事务工厂和关闭事务的方法。

这里写图片描述

SqlSessionFactory的创建主要用到了Builder模式:

Configuration担任导演角色。

这里写图片描述

再来看openSession() 方法:

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), 
    (TransactionIsolationLevel)null, false);
    //可以看到默认的事务隔离级别为空,autoCommit默认为false事务不会自动提交。ExecutorType默认为SIMPLE。
 }

可以看出默认是从我们配置的dataSource中获取SqlSession 。

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
            //获取Environment,即我们在配置文件中用<environments>标签所指定的内容。
            Environment environment = this.configuration.getEnvironment(); 
            //获取TransactionFactory工厂,用来创建Transaction
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            //获取事务
            tx = transactionFactory.newTransaction(e.getDataSource(), level, autoCommit);
            //Executor 的创建
            Executor executor = this.configuration.newExecutor(tx, execType);
            //在这里创建了默认的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;
    }

environment就是我们在mybatis-config.xml中配置在 标签内的东西:断点结果如图。

这里写图片描述

在这里我们配置的是JDBC的事务管理器,所以使用了JDBC的Transaction。

public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, 
                boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
}

在JdbcTransaction默认使用的是slf4j日志接口,所以我们可以通过配置日志,打印出sql语句。

Executor的创建

Executor executor = this.configuration.newExecutor(tx, execType);时进行了Executor的创建,类型为CachingExecutor

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    //两次检查防止executorType为空,默认情况下设置为ExecutorType.SIMPLE
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //批量   TODO 分析
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction); //在这里使用的SimpleExecutor
    }
    if (cacheEnabled) { //默认缓存是开启的,所以会创建CachingExecutor
      executor = new CachingExecutor(executor);
    }

    //注册拦截器链(其实就是一个ArrayList)
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

因为cacheEnabledtrue,所以创建的Executor类型为CachingExecutor。而CachingExecutor中,持有一个delegate类型为SimpleExecutor

这里写图片描述

Interceptor

拦截器链的初始化,
executor = (Executor) interceptorChain.pluginAll(executor);
拦截器链是一个ArrayList.

private final List<Interceptor> interceptors = new ArrayList<Interceptor>();  

SqlSession

接下来看SqlSession。SqlSession中定义了操作数据库的base方法以及对事务的提交和回滚等操作,可以看到mybatis非常灵活,我们可以直接在SqlSession拿到Connection。
同时,SqlSession接口继承自Closeable,而 Closeable extends AutoCloseable ,表示session可以自动被关闭。
例如:我们可以通过拿到Connection并获取操作的表名。

        Connection conn = sqlSession.getConnection();
        log.info(conn.getCatalog());

这里写图片描述


 //在DefaultSqlSession中,持有Configuration,和Executor的引用。
 //所以SqlSession才可以委托Executor具体实现类(例如:CachingExecutor、SimpleExecutor)去执行对应的sql语句。
 //而Configuration则提供类型转换、结果解析等信息。
public class DefaultSqlSession implements SqlSession {

  private Configuration configuration;
  private Executor executor;

  private boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;

  ...

}

至此,SqlSession创建完成。就可以调用接口中的selectOne()、selectList()去执行查询等操作。

总结:

1.首先会读取mybatis-config.xml配置文件及Mapper.xml映射文件。
2.从以上配置文件中解析各个标签中的信息,并将信息注册到Configuration实例中,同时还会初始化默认的参数。
3.通过DefaultSqlSessionFactoryBuilder#build(Configuration config)实例化DefaultSqlSessionFactory,并将Configuration传递给它。并通过Configuration中的连接池、事务隔离级别,以及executor和autoCommit等参数实例化SqlSession接口的实现类DefaultSqlSession。
4.DefaultSqlSession具体实现了SqlSession中定义的接口。并通过自己持有的Executor接口,委托具体的Executor去执行sql语句完成具体的CRUD操作。
这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解

对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。本章将通过以下几点详细介绍MyBatis的初始化过程。     1.MyBatis的初始化做了什么    ...

MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用

前言:学习框架一个比较好的路径阅读源码.本文介绍的SqlSessionFactory和SqlSession.可以通过了解SqlSessionFactory接口和SqlSession接口以及两个的实现类...

深入浅出MyBatis-Sqlsession

前面的章节主要讲mybatis如何解析配置文件,这些都是一次性的过程。从本章开始讲解动态的过程,它们跟应用程序对mybatis的调用密切相关。本章先从sqlsession开始。 创建 正如其名,S...

MyBatis SqlSessionFactory的几种常见创建方式

MyBatis框架主要是围绕着SqlSessionFactory这个类进行的,这个的创建过程如下: 定义一个Configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据...

MyBatis的SqlSessionFactory的创建问题

MyBatis SqlSessionFactory Singleton

Mybatis源码分析之SqlSessionFactory,SqlSession和连接池

简单介绍下mybatis获取SqlSession和进行sql操作的例子 InputStream inputStream = Resources.getResourceAsStream(resou...

spring-mybatis 之SqlSessionFactoryBean

在 MyBatis 中,使用 SqlSessionFactoryBuilder创建SqlSessionFactory ,进而来创建 SqlSession。一旦你获得一个 session 之后,你可以使...

MapperScannerConfigurer之sqlSessionFactory注入方式讲解

MapperScannerConfigurer之sqlSessionFactory注入方式讲解 首先,Mybatis中的有一段配置非常方便,省去我们去写DaoImpl(Dao层实现类)的时间,这个配...

SqlSessionFactory创建SqlSession测试mybatis的sql

SqlSessionFactory创建SqlSession测试mybatis映射文件的sql package com.xuan.mybatis.first; import java.io.IOExc...

Mybatis源码分析(二)- SqlSessionFactory和SqlSession详解

本系列以Mybatis 3.3.X分支源码作为分析源,mybatis源码Git地址:https://github.com/mybatis/mybatis-3.git。  SqlSessionFact...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建
举报原因:
原因补充:

(最多只允许输入30个字)