mybatis-sqlsession解析

概述

mybatis的执行流程(来源于网络):

流程图

前几篇分析过Executor,执行器在StatementHandler上添加了缓存和事务的功能,但api还是比较偏底层,因此需要再Executor上再封装、增强一层,因此就有了SqlSession。

使用实例:

// 构造 SqlSessionFactory
Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);

// 创建SqlSession实例
SqlSession sqlSession = sqlSessionFactory.openSession();

// 执行sql
Object param = new Object();
Object result = sqlSession.selectOne("statementId", param);

跟着实例的顺序解析

SqlSessionFactoryBuilder

SqlSessionFactory构造器,build方法参数为Configuration,

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

其他build的重载版本最终是为了解析获得Configuration。

SqlSessionFactory

SqlSession工厂,就只负责SqlSession的创建。

public interface SqlSessionFactory {
     

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);

  SqlSession openSession(Connection connection);

  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}

创建SqlSession方法分为几类:

  • 指定事务相关参数
  • 指定执行器类型
  • 指定数据库连接
  • 默认

SqlSessionFactory目前就一个实现类 DefaultSqlSessionFactory

DefaultSqlSessionFactory

mybatis-sqlsession解析_第1张图片

所有的重载版本可归为两类:

  • 通过数据源创建
  • 通过数据源连接创建

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);

    //根据 配置、执行器 创建一个新的SqlSession
    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();
  }
}

创建步骤:

  1. 获取事务工厂
  2. 创建事务
  3. 创建执行器
  4. 创建SqlSession

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);
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
     
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
     
    ErrorContext.instance().reset();
  }
}

步骤与FromDataSource一模一样,区别点在于创建事务时,一个通过数据源,一个通过数据源连接,用于Connection传递复用的场景。


SqlSession关于sql执行方法的实现主要还是依靠Executor,因此创建SqlSession的主要点就是创建Executor。

SqlSession

mybatis-sqlsession解析_第2张图片

Executor只有查询和update两个方法,明显对于上层用户是很不方便使用的。

SqlSession在此进行细粒度的拆分,对返回值类型也进行了扩充。

查询分为了:

  • one
  • List
  • Map
  • Cursor
  • ResultHandler

修改分为了:

  • insert
  • update
  • delete

初次之外还有事务、缓存相关的api。

来看下SqlSession的实现类 DefaultSqlSession

DefaultSqlSession

public class DefaultSqlSession implements SqlSession {
     

  // 配置
  private final Configuration configuration;
  // 执行器
  private final Executor executor;
  // 是否自动提交
  private final boolean autoCommit;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
     
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
     
    this(configuration, executor, false);
  }
}

挑几个重点方法看下实现

selectList

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
     
  try {
     
    /*
     * 根据statementId找到唯一的statement即sql语句
     */
    MappedStatement ms = configuration.getMappedStatement(statement);

    /*
     * 直接委托给 executor执行器 来完成sql命令
     */
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
     
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
     
    ErrorContext.instance().reset();
  }
}

update

@Override
public int update(String statement, Object parameter) {
     
  try {
     
    dirty = true;
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.update(ms, wrapCollection(parameter));
  } catch (Exception e) {
     
    throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
  } finally {
     
    ErrorContext.instance().reset();
  }
}

Mapper代理

sql相关方法比较简单,就是对不同的返回值、sql类型做了封装。

想一下,如果直接使用SqlSession的话,调用select、insert等方法,每次需要传入StatementId,这是一个字符串,非常的不方便,万一传错了那么就会报错,编译时还发现不了。

mybatis也想到了这点,所以有更方便使用的方式,那就是为Mapper创建代理对象,直接调用mapper方法自动完成sql的执行。

创建Mapper代理对象的方法就在SqlSession中。

@Override
public <T> T getMapper(Class<T> type) {
     
  return configuration.getMapper(type, this);
}

实际实现是由Configuration完成的。

这里我们来看下这里代理对象是如何实现的

MapperMethod:根据方法、参数类型路由到SqlSession的具体方法

MapperProxy:Mapper代理实现类,缓存MapperMethod,拦截Mapper方法。

MapperProxyFactory:MapperProxy工厂,负责创建MapperProxy。

总结

SqlSession是mybatis提供给上层使用的核心类,提供了比较简单的api,为了让使用更方便,添加了代理的方式,让sql执行更加丝滑。

你可能感兴趣的:(源码解析,java,mybatis,mysql,sql)