Mybatis核心處理層源碼閱讀分析(SqlSession系列組件)

SqlSession是MyBatis核心接口之一,也是MyBatis接口層的主要組成部分,對外提供MyBatis常用的API。mybatis提供了兩個SqlSession接口的實現,分別爲DefaultSqlSession、SqlSessionManager,其中最常用的是DefaultSqlSession。另外,跟前面分析過的源碼mybatis的源碼一樣,mybatis也爲SqlSession提供了相應的工廠接口SqlSessionFactory,及實現該接口的實現DefaultSqlSessionFactory(SqlSessionManager同時實現了SqlSession和SqlSessionFactory接口)。

1 SqlSession

在SqlSession中定義了常用的數據庫操作以及事務的相關操作,爲了方便用戶使用,每種類型的操作都提供了多種重載。

public interface SqlSession extends Closeable {
  // 泛型方法,參數是要執行查詢的sql語句,返回值爲查詢的結果對象
  <T> T selectOne(String statement);

  // 第二個參數表示 需要用戶傳入的實參,即 sql語句綁定的實參
  <T> T selectOne(String statement, Object parameter);

  // 查詢結果有多條記錄,會封裝成 結果對象列表 並返回
  <E> List<E> selectList(String statement);

  // 參數 + 多記錄結果集
  <E> List<E> selectList(String statement, Object parameter);

  // 參數RowBounds主要用於邏輯分頁,邏輯分頁會將所有的結果都查詢到,
  // 然後根據RowBounds中提供的offset和limit值來獲取最後的結果
  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

  // mapKey表示將結果集中的哪一列(如 主鍵列或編碼列)作爲Map的key,
  // value則爲列值 對應的那條記錄
  <K, V> Map<K, V> selectMap(String statement, String mapKey);

  // 多了個parameter參數,其它與上面相同
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

  // 多了個RowBounds參數,其它與上面相同
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

  // 除了返回值是Cursor對象,其它與selectList相同
  <T> Cursor<T> selectCursor(String statement);
  <T> Cursor<T> selectCursor(String statement, Object parameter);
  <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);

  // 查詢出的結果集 將由傳入的ResultHandler對象處理,其它與selectList相同
  void select(String statement, Object parameter, ResultHandler handler);
  void select(String statement, ResultHandler handler);
  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

  // 執行insert語句
  int insert(String statement);
  int insert(String statement, Object parameter);

  // 執行update語句
  int update(String statement);
  int update(String statement, Object parameter);

  // 執行delete語句
  int delete(String statement);
  int delete(String statement, Object parameter);

  // 提交事務
  void commit();
  void commit(boolean force);

  // 回滾事務
  void rollback();
  void rollback(boolean force);

  // 將對數據庫的操作請求 刷到數據庫
  List<BatchResult> flushStatements();

  // 關閉當前session
  void close();

  // 清空緩存
  void clearCache();

  // 獲取Configuration對象
  Configuration getConfiguration();

  // 獲取type對應的Mapper對象
  <T> T getMapper(Class<T> type);

  // 獲取該SqlSession對應的數據庫連接
  Connection getConnection();
}

1.1 DefaultSqlSession

DefaultSqlSession是單獨使用MyBatis進行開發時,最常用的SqISession接口實現。其實現了SqISession接口中定義的方法,及各方法的重載。select()系列方法、selectOne()系列方法、selectList()系列方法、selectMap()系列方法之間的調用關係如下圖,殊途同歸,它們最終都會調用Executor的query()方法。
在這裏插入圖片描述
上述重載方法最終都是通過調用Executor的query(MappedStatement, Object, RowBounds,ResultHandler)方法實現數據庫查詢操作的,但各自對結果對象進行了相應的調整,例如:selectOne()方法是從結果對象集合中獲取了第一個元素返回;selectMap()方法會將List類型的結果集 轉換成Map類型集合返回;select()方法是將結果集交由用戶指定的ResultHandler對象處理,且沒有返回值;selectList()方法則是直接返回結果對象集合。
DefaultSqlSession的insert()方法、update()方法、delete()方法也有多個重載,它們最後都是通過調用DefaultSqlSession的update(String, Object)方法實現的,該重載首先會將dirty字段置爲true,然後再通過Executor的update()方法完成數據庫修改操作。
DefaultSqlSession的commit()方法、rollback()方法以及close()方法都會調用Executor中相應的方法,其中就會涉及清空緩存的操作,之後就會將dirty字段設置爲false。
上述的dirty字段主要在isCommitOrRollbackRequired()方法中,與autoCommit字段以及用戶傳入的force參數共同決定是否提交/回滾事務。該方法的返回值將作爲Executor的commit()方法和rollback()方法的參數。

  private boolean isCommitOrRollbackRequired(boolean force) {
    return (!autoCommit && dirty) || force;
  }

2 SqlSessionFactory

SqlSessionFactory負責創建SqlSession對象,其中包含了多個openSession()方法的重載,可以通過其參數指定事務的隔離級別、底層使用Executor的類型、以及是否自動提交事務等方面的配置。

public interface SqlSessionFactory {

  // 提供了openSession()方法的多種重載,根據相應的參數 可以指定事務的隔離級別、
  // 底層使用的Executor類型、以及是否自動提交事務等配置
  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();
}

2.1 DefaultSqlSessionFactory

DefaultSqlSessionFactory是SqlSessionFactory接口的默認實現,主要提供了兩種創建DefaultSqlSession對象的方式,一種方式是通過數據源獲取數據庫連接,並創建Executor對象以及DefaultSqlSession對象;另一種方式是用戶提供數據庫連接對象,DefaultSqlSessionFactory根據該數據庫連接對象獲取autoCommit屬性,創建Executor對象以及DefaultSqlSession對象。

DefaultSqISessionFactory提供的所有openSession()方法重載都是基於上述兩種方式創建DefaultSqlSession對象的。

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 獲取配置的Environment對象
      final Environment environment = configuration.getEnvironment();
      // 從environment中獲取TransactionFactory對象,如果沒有,就創建一個ManagedTransactionFactory實例並返回
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 從事務工廠中獲取一個事務對象
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根據事務對象tx和配置的Executor類型execType創建Executor實例
      // ExecutorType是個枚舉類型,有三個值 SIMPLE, REUSE, BATCH,分別對應了
      // SimpleExecutor、ReuseExecutor、BatchExecutor
      final Executor executor = configuration.newExecutor(tx, execType);
      // 創建DefaultSqlSession對象
      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();
    }
  }

  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        // 根據當前連接對象獲取autoCommit屬性(是否自動提交事務)
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        autoCommit = true;
      }
      // 除了獲取autoCommit屬性的方式和上面不一樣外,下面的處理都與上面完全相同
      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();
    }
  }

  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

  private void closeTransaction(Transaction tx) {
    if (tx != null) {
      try {
        tx.close();
      } catch (SQLException ignore) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

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

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

  @Override
  public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
  }

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

  @Override
  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return openSessionFromDataSource(execType, level, false);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
    return openSessionFromDataSource(execType, null, autoCommit);
  }

  @Override
  public SqlSession openSession(Connection connection) {
    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, Connection connection) {
    return openSessionFromConnection(execType, connection);
  }

  @Override
  public Configuration getConfiguration() {
    return configuration;
  }
}

2.2 SqlSessionManager

SqlSessionManager同時實現了SqlSession接口和SqlSessionFactory接口,所以同時提供了SqlSessionFactory創建SqlSession對象,以及SqlSession操縱數據庫的功能。

SqlSessionManager與DefaultSqlSessionFactory的主要不同點SqlSessionManager 提供了兩種模式,第一種模式與DefaultSqlSessionFactory的行爲相同,同一線程每次通過SqlSessionManager對象訪問數據庫時,都會創建新的SqlSession對象完成數據庫操作。第二種模式是SqlSessionManager通過localSqlSession這ThreadLocal 變量,記錄與當前線程綁定的SqlSession對象,供當前線程循環使用,從而避免在同一線程多次創建SqlSession對象帶來的性能損失。

SqlSessionManager的構造方法是唯一且私有的,如果要創建SqlSessionManager對象,需要調用其newInstance()方法(但需要注意的是,這不是單例模式,因爲每次調用newInstance()方法都返回了一個新的對象)。

SqlSessionManager的openSession()系列方法,都是通過直接調用其持有的
DefaultSqlSessionFactory實例來實現的。

public class SqlSessionManager implements SqlSessionFactory, SqlSession {

  // 通過持有DefaultSqlSessionFactory對象 來產生SqlSession對象
  private final SqlSessionFactory sqlSessionFactory;

  // 用於記錄一個與當前線程綁定的SqlSession對象
  private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();

  // localSqlSession中記錄的SqlSession對象的代理對象(JDK動態代理)
  // SqlSessionManager初始化時 生成本代理對象,可以看下 下面的構造函數
  private final SqlSession sqlSessionProxy;

  // 私有的構造函數,也是SqlSessionManager唯一的構造函數
  private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
    // 傳入的這個SqlSessionFactory對象 往往是DefaultSqlSessionFactory的實例
    this.sqlSessionFactory = sqlSessionFactory;
    // JDK動態代理生成代理對象,可以看得出,SqlSessionInterceptor一定實現了
    // InvocationHandler接口
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[]{SqlSession.class},
        new SqlSessionInterceptor());
  }

  // 通過newInstance()方法創建SqlSessionManager對象,有多種重載,
  // 但最後都是new了一個DefaultSqlSessionFactory的實例
  public static SqlSessionManager newInstance(Reader reader) {
    return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));
  }

  public static SqlSessionManager newInstance(Reader reader, String environment) {
    return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));
  }

  public static SqlSessionManager newInstance(Reader reader, Properties properties) {
    return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties));
  }

  public static SqlSessionManager newInstance(InputStream inputStream) {
    return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null));
  }

  public static SqlSessionManager newInstance(InputStream inputStream, String environment) {
    return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null));
  }

  public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) {
    return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties));
  }

  public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionManager(sqlSessionFactory);
  }

  // openSession()系列方法都是通過當前SqlSessionManager對象持有的
  // DefaultSqlSessionFactory實例的openSession()實現的
  @Override
  public SqlSession openSession() {
    return sqlSessionFactory.openSession();
  }

  @Override
  public SqlSession openSession(boolean autoCommit) {
    return sqlSessionFactory.openSession(autoCommit);
  }

  @Override
  public SqlSession openSession(Connection connection) {
    return sqlSessionFactory.openSession(connection);
  }

  @Override
  public SqlSession openSession(TransactionIsolationLevel level) {
    return sqlSessionFactory.openSession(level);
  }

  @Override
  public SqlSession openSession(ExecutorType execType) {
    return sqlSessionFactory.openSession(execType);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
    return sqlSessionFactory.openSession(execType, autoCommit);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return sqlSessionFactory.openSession(execType, level);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, Connection connection) {
    return sqlSessionFactory.openSession(execType, connection);
  }
}

SqlSessionManager中實現的SqlSession接口方法,例如select ()系列方法、update()系列方法等,都是直接調用sqlSessionProxy代理對象對應的方法實現的。在創建該代理對象時使用的InvocationHandler對象是SqlSessionlnterceptor,它是SqISessionManager的內部類。

  private class SqlSessionInterceptor implements InvocationHandler {

    public SqlSessionInterceptor() { }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 獲取 與當前線程綁定的SqlSession
      final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
      // 如果有綁定的SqlSession對象
      if (sqlSession != null) { // 模式二
        try {
          // 調用真正的sqlSession對象,完成數據庫操作
          return method.invoke(sqlSession, args);
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      // 如果沒有綁定的SqlSession對象
      } else { // 模式一
        // 創建一個新的SqlSession對象
        final SqlSession autoSqlSession = openSession();
        try {
          // 通過反射調用該SqlSession對象的方法,完成數據庫操作
          final Object result = method.invoke(autoSqlSession, args);
          // 提交事務
          autoSqlSession.commit();
          return result;
        } catch (Throwable t) {
          // 出異常就回滾
          autoSqlSession.rollback();
          throw ExceptionUtil.unwrapThrowable(t);
        } finally {
          // 關閉該SqlSession對象
          autoSqlSession.close();
        }
      }
    }
  }

通過對SqlSessionlnterceptor的分析可知,第一種模式中新建的SqlSession在使用完成後會立即關閉。在第二種模式中,與當前線程綁定的SqISession對象需要先通過SqlSessionManager的startManagedSession()方法進行設置,此方法也存在多種重載,但都彼此相似 且簡單。

  public void startManagedSession() { 
    this.localSqlSession.set(openSession()); 
  }

  public void startManagedSession(boolean autoCommit) {
    this.localSqlSession.set(openSession(autoCommit));
  }

  public void startManagedSession(Connection connection) {
    this.localSqlSession.set(openSession(connection));
  }

  public void startManagedSession(TransactionIsolationLevel level) {
    this.localSqlSession.set(openSession(level));
  }

  public void startManagedSession(ExecutorType execType) {
    this.localSqlSession.set(openSession(execType));
  }

  public void startManagedSession(ExecutorType execType, boolean autoCommit) {
    this.localSqlSession.set(openSession(execType, autoCommit));
  }

  public void startManagedSession(ExecutorType execType, TransactionIsolationLevel level) {
    this.localSqlSession.set(openSession(execType, level));
  }

  public void startManagedSession(ExecutorType execType, Connection connection) {
    this.localSqlSession.set(openSession(execType, connection));
  }

  public boolean isManagedSessionStarted() {
    return this.localSqlSession.get() != null;
  }

當需要提交/回滾事務,或關閉IocalSqlSession中記錄的SqlSession對象時,需要通過SqlSessionManager的commit()、rollback()以及close()方法完成,其中會先檢測當前線程是否綁定了SqlSession對象,如果未綁定則拋出異常,如果綁定了則調用該SqlSession對象的相應方法。

  @Override
  public void clearCache() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot clear the cache.  No managed session is started.");
    }
    sqlSession.clearCache();
  }

  @Override
  public void commit() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot commit.  No managed session is started.");
    }
    sqlSession.commit();
  }

  @Override
  public void commit(boolean force) {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot commit.  No managed session is started.");
    }
    sqlSession.commit(force);
  }

  @Override
  public void rollback() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot rollback.  No managed session is started.");
    }
    sqlSession.rollback();
  }

  @Override
  public void rollback(boolean force) {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot rollback.  No managed session is started.");
    }
    sqlSession.rollback(force);
  }

  @Override
  public List<BatchResult> flushStatements() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot rollback.  No managed session is started.");
    }
    return sqlSession.flushStatements();
  }

  @Override
  public void close() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot close.  No managed session is started.");
    }
    try {
      sqlSession.close();
    } finally {
      localSqlSession.set(null);
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章