mybatis 四大核心接口對象

mybatis在執行期間,主要有四大核心接口對象:

  1. 執行器Executor,執行器負責整個SQL執行過程的總體控制。
  2. 語句處理器StatementHandler,語句處理器負責和JDBC層具體交互,包括prepare語句,執行語句,以及調用ParameterHandler.parameterize()設置參數。
  3. 參數處理器ParameterHandler,參數處理器負責PreparedStatement入參的具體設置。
  4. 結果集處理器ResultSetHandler,結果處理器負責將JDBC查詢結果映射到java對象。


1、執行器Executor

所有我們在應用層通過sqlSession執行的各類selectXXX和增刪改操作在做了動態sql和參數相關的封裝處理後,都被委託給具體的執行器去執行,包括一、二級緩存的管理,事務的具體管理。執行器比較像是sqlSession下的各個策略工廠實現,用戶通過配置決定使用哪個策略工廠。
mybatis提供了兩種類型的執行器,緩存執行器(CachingExecutor)與非緩存執行器(SimpleExecutor、ReuseExecutor、BatchExecuto。默認是SIMPLE),是否使用緩存執行器則是通過執行cacheEnabled控制的,默認是true。
  緩存執行器不是真正功能上獨立的執行器,而是非緩存執行器的裝飾器模式。我們先來看非緩存執行器。非緩存執行器又分爲三種,這三種類型的執行器都基於基礎執行器BaseExecutor,基礎執行器完成了大部分的公共功能


Mybatis有三種基本的Executor執行器:

  1. SimpleExecutor:每執行一次update或query,就開啓一個Statement對象,用完立刻關閉Statement對象。注:增刪改爲update,查詢爲query。
  2. ReuseExecutor:執行update或query,以sql作爲key查找Statement對象,存在就使用,不存在就創建,用完後,不關閉Statement對象,而是放置於Map內,供下一次使用。簡言之,就是重複使用Statement對象。
  3. BatchExecutor:執行update(沒有query,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。作用範圍:Executor的這些特點,都嚴格限制在SqlSession生命週期範圍內。


Mybatis中如何指定使用哪一種Executor執行器?
1、在Mybatis配置文件中,可以指定默認的ExecutorType執行器類型,<setting name="defaultExecutorType" value="SIMPLE"/>
2、手動給DefaultSqlSessionFactory的創建SqlSession的方法傳遞ExecutorType類型參數。SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) 


2、StatementHandler:

mybatis包含了三種類型的StatementHandler實現,分別用於JDBC對應的PrepareStatement,Statement以及CallableStatement。BaseStatementHandler是這三種類型語句處理器的抽象父類,封裝了一些實現細節比如設置超時時間、結果集每次提取大小等操作。

3、ParameterHandler

ParameterHandler的接口定義如下:

public interface ParameterHandler {

  Object getParameterObject();
  void setParameters(PreparedStatement ps);

}

ParameterHandler只有一個默認實現DefaultParameterHandler.

4、ResultSetHandler

結果集處理器,顧名知義,就是用了對查詢結果集進行處理的,目標是將JDBC結果集映射爲業務對象。其接口定義如下:

public interface ResultSetHandler {

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

接口中定義的三個接口分別用於處理常規查詢的結果集,遊標查詢的結果集以及存儲過程調用的出參設置。和參數處理器一樣,結果集處理器也只有一個默認實現DefaultResultSetHandler。結果集處理器的功能包括對象的實例化、屬性自動匹配計算、常規屬性賦值、嵌套ResultMap的處理、嵌套查詢的處理、鑑別器結果集的處理等,每個功能我們在分析SQL語句執行selectXXX的時候都詳細的講解過了,具體可以參見selectXXX部分。

測試用例:

package hong.helloWorld.orm.dao.upms;
public interface UserMapper {
    List<User> selectByName(@Param("name")  String name);
}

 


<mapper namespace="hong.helloWorld.orm.dao.upms.UserMapper">
    <resultMap id="BaseResultMap" type="hong.helloWorld.orm.entity.upms.User">
        <id column="ID" jdbcType="VARCHAR" property="id" />
        <result column="NAME" jdbcType="VARCHAR" property="name"  />
        <result column="AGE" jdbcType="DECIMAL" property="age" />
        <result column="BIRTHDAY" jdbcType="VARCHAR" property="birthday" />
        <result column="CREATE_TIME" jdbcType="DECIMAL" property="createTime" />
    </resultMap>
    
    <select id="selectByName" resultMap="BaseResultMap">
        select * from TEST_USER where  name=#{name}
    </select>
</mapper>
    @Test
    public void Test() throws Exception {

        String resource = "hong/helloWorld/Configuration.xml";
        Reader reader = Resources.getResourceAsReader(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sqlSessionFactory.openSession();

        Map parameter=new HashMap();
        parameter.put("name","xiaoming");
        List<User> userList = session.selectList("hong.helloWorld.orm.dao.upms.UserMapper.selectByName",parameter);

        session.close();
        
    }


現在就來研究一下代碼的執行過程:

在 Test 方法中,第一步,SqlSessionFactoryBuilder讀取 mybaits 的配置文件,創建 SqlSessionFactory;第二步,SqlSessionFactory 創建 SqlSession;第三步,SqlSession 執行數據操作;第四步,關閉。

第一步:SqlSessionFactoryBuilder讀取 mybaits 的配置文件,創建 SqlSessionFactory。

public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } 
  }

  public SqlSessionFactory build(Configuration config) {
    //mybatis配置文件信息都被存放在Configuration對象中。這裏創建的是DefaultSqlSessionFactory   
    return new DefaultSqlSessionFactory(config);
  }

}

第二步,SqlSessionFactory 創建 SqlSession。

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    @Override
    public SqlSession openSession() {
        // 使用默認的執行器類型(默認是SIMPLE),默認隔離級別,非自動提交 委託給openSessionFromDataSource方法
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }

    /**
     * 通過數據源創建SqlSession
     * @param execType   mybatis提供了三種執行器類型:SIMPLE, REUSE, BATCH。
     * @param level      事務隔離級別
     * @param autoCommit 是否自動提交
     * @return
     */
    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);
        } 
    }
}

第三步,SqlSession 執行數據操作。

public class DefaultSqlSession implements SqlSession{

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

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } 
  }
}

DefaultSqlSession 會將查詢操作交給 Executor對象去完成。交給 Executor前會獲取 MappedStatement 對象,MappedStatement封裝了 sql 的所有信息。

MappedStatement:

由於這裏沒有開啓二級緩存,使用的是默認的Executor實現類SimpleExecutor。

public abstract class BaseExecutor implements Executor {
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 首先根據傳遞的參數獲取BoundSql對象,對於不同類型的SqlSource,對應的getBoundSql實現不同.
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 創建緩存key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        // 委託給重載的query
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 
        //如果需要刷新緩存(默認DML需要刷新,也可以語句層面修改), 且queryStack(應該是用於嵌套查詢的場景)=0
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            // 如果<select>節點配置了flushCache爲true的話,清空一級緩存
            clearLocalCache();
        }
        List<E> list;
        try {
            queryStack++;
            // 如果查詢不需要應用結果處理器,則先從緩存獲取,這樣可以避免數據庫查詢。
            // 我們後面會分析到localCache是什麼時候被設置進去的.
            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) {
   
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                // issue #482
                // 如果設置了一級緩存是STATEMENT級別而非默認的SESSION級別,一級緩存就去掉了
                clearLocalCache();
            }
        }
        return list;
    }

    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的
            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;
    }
    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
            throws SQLException;
}
public class SimpleExecutor extends BaseExecutor {

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 根據上下文參數和具體的執行器new一個StatementHandler, 其中包含了所有必要的信息,比如結果處理器、參數處理器、執行器等等,
      // 主要有三種類型的語句處理器UNPREPARE、PREPARE、CALLABLE。默認是PREPARE類型,通過mapper語句上的statementType屬性進行設置,
      // 一般除了存儲過程外不應該設置
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 這一步是真正和JDBC打交道,獲取statement,也包含了對ParameterHandler 的調用。
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
}

 

public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;
  //RoutingStatementHandler根據 statementType 創建不同的 StatementHandler
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }


  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.<E>query(statement, resultHandler);
  }
}

 

public class PreparedStatementHandler extends BaseStatementHandler {

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();//執行 sql
    //ResultSetHandler對結果集進行處理
    return resultSetHandler.<E> handleResultSets(ps);
  }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章