1. 程式人生 > >mybatis(一) sqlSessionFactory和sqlSession的建立

mybatis(一) sqlSessionFactory和sqlSession的建立

最近想深入學習一下mybatis,想通過看mybatis的原始碼,瞭解mybatis的整個工作流程,熟悉mybatis的各種細節。

使用mybatis的方式不同,sqlSessionFactory的建立方法也不同,具體可以看SqlSessionFactoryBuilder的原始碼,裡面有很多過載的build方法。本文是在SpringBoot環境下,基於mapper介面使用mybatis,在啟動專案中的某個時間點會呼叫SqlSessionFactoryBuilder中的如下方法建立SqlSessionFactory:

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

這個方法需要一個Configuration型別的引數,所以需要先建立Configuration物件,雖然不同的build方法實現有不同,但是核心都是解析mybatis.xml配置檔案和mapper.xml的配置檔案。

 建立完sqlSessionFactory後,在具體執行增刪改查的時候,還需要建立sqlSession

在使用mapper介面增刪改查的方式中,sqlSession的建立是在執行增刪改查的過程中,在sqlSession的代理方法中建立的。

如下:sqlSessionTemplate會呼叫sqlSessionProxy的一個代理方法

public <E> List<E> selectList(String statement, Object parameter) {
  return this.sqlSessionProxy.<E> selectList(statement, parameter);
}

sqlSessionProxy的建立方法如下,所以會呼叫SqlSessionInterceptor中的invoke方法

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  notNull(executorType, "Property 'executorType' is required");

  this.sqlSessionFactory = sqlSessionFactory;
  this.executorType = executorType;
  this.exceptionTranslator = exceptionTranslator;
  this.sqlSessionProxy = (SqlSession) newProxyInstance(
      SqlSessionFactory.class.getClassLoader(),
      new Class[] { SqlSession.class },
      new SqlSessionInterceptor());
}

SqlSessionInterceptor是SqlSessionTemplate中的一個private內部類, invoke方法中呼叫了

SqlSessionUtils.getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)

  (由於直接匯入了靜態方法,所以呼叫的時候沒有在方法前面加上類名,import static org.mybatis.spring.SqlSessionUtils.getSqlSession;)

private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    SqlSession sqlSession = getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType,
        SqlSessionTemplate.this.exceptionTranslator);
    try {
      Object result = method.invoke(sqlSession, args);
      if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
        // force commit even on non-dirty sessions because some databases require
        // a commit/rollback before calling close()
        sqlSession.commit(true);
      }
      return result;
    } catch (Throwable t) {
      Throwable unwrapped = unwrapThrowable(t);
      if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
        // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        sqlSession = null;
        Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
        if (translated != null) {
          unwrapped = translated;
        }
      }
      throw unwrapped;
    } finally {
      if (sqlSession != null) {
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }
} sqlSessionUtils.getSqlSession()後面的時序圖如下:

在DefaultSqlSessionFactory的openSessionFromDataSource方法中,用 configuration.newExecutor(tx, execType)

建立了mybatis四大物件之一的executor物件,在執行增刪改查的時候需要用到executor

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

newExecutor方法如下,executor = (Executor) interceptorChain.pluginAll(executor),這段程式碼將executor封裝成了攔截器鏈,是mybati的executor外掛的原理。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  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);
  }
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}