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

来源:互联网 发布:java项目需求文档模板 编辑:程序博客网 时间:2024/05/18 11:16

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操作。
这里写图片描述

0 0
原创粉丝点击