1. 程式人生 > 資料庫 >mybatis原始碼閱讀(一):SqlSession和SqlSessionFactory

mybatis原始碼閱讀(一):SqlSession和SqlSessionFactory

轉載自  mybatis原始碼閱讀(一):SqlSession和SqlSessionFactory 

一、介面定義

    聽名字就知道這裡使用了工廠方法模式,SqlSessionFactory負責建立SqlSession物件。其中開發人員最常用的就是DefaultSqlSession

(1)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);
  // 第三個引數用於限制解析結果集的範圍
  <E> List<E> selectList(String statement,Object parameter,RowBounds rowBounds);

  /**
   *
   *  selectMap 方法的原理和引數都與selectList方法型別,但結果集會被對映成Map物件返回
   *  其中mapKey引數指定了結果集哪一列作為map的key,其他引數同上
   */
  <K,V> Map<K,V> selectMap(String statement,String mapKey);
  <K,String mapKey,RowBounds rowBounds);

  // 返回值是遊標物件,引數同上
  <T> Cursor<T> selectCursor(String statement);
  <T> Cursor<T> selectCursor(String statement,Object parameter);
  <T> Cursor<T> selectCursor(String statement,RowBounds rowBounds);

  // 查詢的結果物件將由此處指定的handler物件處理,其餘引數同上
  void select(String statement,ResultHandler handler);
  void select(String statement,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
  @Override
  void close();

  // 清空session 快取
  void clearCache();

  // 獲取Configuration 物件
  Configuration getConfiguration();

  // 獲取type 物件的Mapper物件
  <T> T getMapper(Class<T> type);

  // 獲取該Sqlsession 物件的資料庫連線
  Connection getConnection();
}

SqlSession資料庫的C、R、U、D及事務處理介面,你懂的。

(2)SqlSessionFactory

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實現類:DefaultSqlSession和SqlSessionManager

SqlSessionFactory實現類:DefaultSqlSessionFactory和SqlSessionManager

(3)DefaultSqlSession

@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();
  }
}
@Override
public <E> List<E> selectList(String statement,RowBounds rowBounds) {
  try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    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();
  }
}

這裡主要看這兩個方法,因為delete和insert最終執行掉用的都是update方法,查詢就更不用說了。從程式碼上看都是從configuration物件中獲取MappedStatement 物件 然後把事情交給小弟Executor去執行,這裡用了很典型的策略設計模式,這個關於Executor 後面介紹。

(4)DefaultSqlSessionFactory

/**
 * 通過資料來源獲取資料庫連線,並建立Executor以及DefaultSqlSession物件
 */
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();
  }
}

/**
 *  使用者提供資料庫連線物件,使用該資料庫連線物件建立Executor和DefaultSqlSession物件
 */
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,autoCommit);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e,e);
  } finally {
    ErrorContext.instance().reset();
  }
}

DefaultSqlSessionFactory主要提供了兩種建立DefaultSqlSession物件的方式,一種是通過資料來源獲取資料庫連線,並建立Executor以及DefaultSqlSession物件,另一種是使用者提供資料庫連線物件,使用該資料庫連線物件建立Executor和DefaultSqlSession物件。

(5)SqlSessionManager

SqlSessionManager同時實現SqlSession介面和SqlSessionFactory介面,也就是同時提供了建立SqlSession物件以及SqlSession物件操作資料庫的功能。SqlSessionManager與DefaultSqlSessionFactory的主要不同點是SqlSessionManager提供了兩種模式,一種是和DefaultSqlSessionFactory的行為相同,同一執行緒每次訪問資料庫就都會建立新的DefaultSqlSession,第二種是通過ThreadLocal變數記錄當前執行緒的SqlSession物件,避免同一執行緒多次建立SqlSession物件。至於使用動態代理的目的,是為了通過攔截器InvocationHandler,增強目標target的方法呼叫。

private final SqlSessionFactory sqlSessionFactory;
// SqlSession的代理物件 會使用JDK的動態代理方式實現
private final SqlSession sqlSessionProxy;

/**
 * ThreadLocal 變數,記錄一個與當前執行緒繫結的SqlSession物件
 * localSqlSession 中記錄的SqlSession物件的代理物件,在SqlSessionManager初始化的時候
 */
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
/**
 * SqlSessionManager 的私有構造方法
 */
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
  this.sqlSessionFactory = sqlSessionFactory;
  // 使用動態代理生成SqlSession的代理物件
  this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
      SqlSessionFactory.class.getClassLoader(),new Class[]{SqlSession.class},new SqlSessionInterceptor());
}
  /**
   * 通過newInstance方法建立SqlSessionManager物件
   * @param reader
   * @return
   */
  public static SqlSessionManager newInstance(Reader reader) {
      return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader,null,null));
  }
// 內部類
private class SqlSessionInterceptor implements InvocationHandler {
      public SqlSessionInterceptor() {
          // Prevent Synthetic Access
      }

      @Override
      public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
          // 獲取當前執行緒繫結的SqlSession物件
          final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
          if (sqlSession != null) {// 第二種模式
              try {
                  return method.invoke(sqlSession,args);
              } catch (Throwable t) {
                  throw ExceptionUtil.unwrapThrowable(t);
              }
          } else {// 第一種模式 建立新的SqlSession
              final SqlSession autoSqlSession = openSession();
              try {
                  final Object result = method.invoke(autoSqlSession,args);
                  autoSqlSession.commit();
                  return result;
              } catch (Throwable t) {
                  autoSqlSession.rollback();
                  throw ExceptionUtil.unwrapThrowable(t);
              } finally {
                  autoSqlSession.close();
              }
          }
      }
}