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

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的工程结构如图:
mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第1张图片

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

    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文件的校验,以及从标签解析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。

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第2张图片

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第3张图片

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

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第4张图片

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

Configuration担任导演角色。

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第5张图片

再来看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,即我们在配置文件中用标签所指定的内容。
            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中配置在 标签内的东西:断点结果如图。

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第6张图片

在这里我们配置的是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

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第7张图片

Interceptor

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

private final List interceptors = new ArrayList();  

SqlSession

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

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

mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第8张图片


 //在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> 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源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建_第9张图片

你可能感兴趣的:(框架学习,mybatis源码)