【MyBatis源码学习】代理阶段


一、Mybatis 的接口层

1.SqlSession

sqlSession总结

"图1"

总之一句话:

/**
 * The primary Java interface for working with MyBatis.
 * Through this interface you can execute commands, get mappers and manage transactions.
 *
 * @author Clinton Begin
 */

翻译过来,就是:对外访问统一的接口,通过这些接口,完成读写命令,获取映射和管理事务。

UML:

"图2"

SqlSession查询接口嵌套关系

"SqlSession查询接口嵌套关系"

2.SqlSessionFactory

SqlSessionFactory与SqlSession关系UML图

"SqlSessionFactory与SqlSession关系UML图"

SqlSessionFactory 使用工厂模式创建SqlSession,其默认的实现类为DefaultSqlSessionFactory,其中获取SqlSession 的核心方法为openSessionFromDataSource(ExecutorType, TransactionIsolationLevel, boolean),在这个方法中从configuration 中获取的TransactionFactory 是典型的策略模式的应用。运行期,TransactionFactory 接口的实现,是由配置文件配置决定的,可配置选项包括:JDBC、Managed,可根据需求灵活的替换TransactionFactory 的实现。

二、binding 模块分析

0.MyBatis两种编程模型

(1) 使用mapper接口编程,就可以访问数据库;

@Test
// 程序员喜欢的风格
public void quickStart() throws IOException {
    // 2.获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 3.获取对应mapper
    TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
    // 4.执行查询语句并返回单条数据
    TUser user = mapper.selectByPrimaryKey(2);
    System.out.println(user);
}

(2) 使用sqlsession对外提供数据库的访问;

@Test
// 原始风格,ibatis编程模型 本质分析
public void originalOperation() throws IOException {
    // 2.获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 3.执行查询语句并返回结果
    TUser user = sqlSession.selectOne("com.taeyeon.mybatis.mapper.TUserMapper.selectByPrimaryKey", 2);
    System.out.println(user.toString());
}

可以比较以下两种编程风格,发现,直接使用SqlSession 进行数据库开发存在代码可读性差、可维护性差的问题,所以我们很少使用,而是使用Mapper接口的方式进行数据库的开发。实现的基础涉及配置文件的解读以及动态代理的运用。

1.核心类

"图3"

2.运行流程

"图4"

三、举个栗子

demo如下:

@Test
// 程序员喜欢的风格
public void quickStart() throws IOException {
    // 2.获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 3.获取对应mapper
    TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
    // 4.执行查询语句并返回单条数据
    TUser user = mapper.selectByPrimaryKey(2);
    System.out.println(user);
}

1.打开一个会话

SqlSession sqlSession = sqlSessionFactory.openSession();

2.DefaultSqlSessionFactory

有两个实现类,主要看DefaultSqlSessionFactory.

"图5"

点进去看看openSession的方法内容

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

关键瞧瞧openSessionFromDataSource这个方法干了什么。

/**
  * 从数据源获取数据库连接
  *
  * @param execType   执行类型,ExecutorType主要有三种类型:SIMPLE, REUSE, BATCH,默认是SIMPLE,都在枚举类ExecutorType里面
  * @param level      事务隔离级别,都在枚举类TransactionIsolationLevel中定义
  * @param autoCommit 是否自动提交,主要是事务提交的设置
  * @return SqlSession
  */
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
   Transaction tx = null;
   try {
   	//获取mybatis配置文件中的environment对象
     final Environment environment = configuration.getEnvironment();
     //从environment获取transactionFactory对象
     final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
     //创建事务对象
     tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
     //根据配置创建executor
     final Executor executor = configuration.newExecutor(tx, execType);
     //创建DefaultSqlSession
     return new DefaultSqlSession(configuration, executor, autoCommit);
   } catch (Exception e) {
     // may have fetched a connection so lets call close()
     closeTransaction(tx);
     throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
   } finally {
     ErrorContext.instance().reset();
   }
 }

重点看看新创建的执行器和DefaultSqlSession。

3.newExecutor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //BatchExecutor、SimpleExecutor和CachingExecutor三者之一
    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);
    }
    //如果有<cache>节点,通过装饰器,添加二级缓存的能力
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //通过interceptorChain遍历所有的插件为executor增强,添加插件的功能
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

4.DefaultSqlSession

再去瞄下DefaultSqlSession干了啥。

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

DefaultSqlSession实现了SqlSession的方法。

"图6"

当执行以下代码时,调用的其实就是DefaultSqlSession类的selectOne方法(最终调用该类下的selectList方法):

@Override
public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  List<T> list = this.<T>selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    //从configuration中获取要执行的sql语句的配置信息
    MappedStatement ms = configuration.getMappedStatement(statement);
    //通过executor执行语句,并返回指定的结果集
    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();
  }
}

5.Executor

先大致了解下Executor的类继承关系。

"图7"

所以,我们重点看看这段代码的奥秘。

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

先看BaseExecutor.

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取sql语句信息,包括占位符,参数等信息
  BoundSql boundSql = ms.getBoundSql(parameter);
  //拼装缓存的key值,这里后续讲讲key的生成规则
  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {//检查当前executor是否关闭
    throw new ExecutorException("Executor was closed.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {//非嵌套查询,并且FlushCache配置为true,则需要清空一级缓存
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;//查询层次加一
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//查询以及缓存
    if (list != null) {
  	 //针对调用存储过程的结果处理
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
  	 //缓存未命中,从数据库加载数据
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {//延迟加载处理
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {//如果当前sql的一级缓存配置为STATEMENT,查询完既清空一集缓存
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}

我们先看缓存未命中的部分,即queryFromDatabase方法。

//真正访问数据库获取结果的方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);//在缓存中添加占位符
  try {
    //调用抽象方法doQuery,方法查询数据库并返回结果,可选的实现包括:simple、reuse、batch
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    localCache.removeObject(key);//在缓存中删除占位符
  }
  localCache.putObject(key, list);//将真正的结果对象添加到一级缓存
  if (ms.getStatementType() == StatementType.CALLABLE) {//如果是调用存储过程
    localOutputParameterCache.putObject(key, parameter);//缓存输出类型结果参数
  }
  return list;
}

其最终调用SimpleExecutor的doQuery方法。

@Override
//查询的实现
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    //获取configuration对象
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler对象,
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    //StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
    stmt = prepareStatement(handler, ms.getStatementLog());
    //通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

看看doQuery方法的内部执行步骤:
(1)获取配置信息对象。
(2)通过配置对象获取一个新的StatementHandler,该类主要用来处理一次SQL操作。
(3)预处理StatementHandler对象,得到Statement对象。
(4)传入Statement和结果处理对象,通过StatementHandler的query方法来执行SQL,并对执行结果进行处理。

先看看newStatementHandler干了啥。

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
	//创建RoutingStatementHandler对象,实际由statmentType来指定真实的StatementHandler来实现
	StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
	//加入插件
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

瞧瞧StatementHandler的小弟们。

"图8"

总结下StatementHandler是干啥的,以及它的小弟们分别能干啥。

"图9"

再看看prepare干了啥。

stmt = handler.prepare(connection, transaction.getTimeout());

主要是BaseStatementHandler干活儿.

@Override
//使用模板模式,定义了获取Statement的步骤,其子类实现实例化Statement的具体的方式;
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    //通过不同的子类实例化不同的Statement,分为三类:simple(statement)、prepare(prepareStatement)、callable(CallableStatementHandler)
    statement = instantiateStatement(connection);
    //设置超时时间
    setStatementTimeout(statement, transactionTimeout);
    //设置数据集大小
    setFetchSize(statement);
    return statement;
  } catch (SQLException e) {
    closeStatement(statement);
    throw e;
  } catch (Exception e) {
    closeStatement(statement);
    throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
  }
}

最后执行query方法

handler.<E>query(stmt, resultHandler);

获得结果集。(这里面有结果集的处理,后续再讲。)


文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录