MyBatis執行sql的整個流程


1.  mybatis的幾大“組件”

我這里說的“組件”,可以理解為Mybatis執行過程中的很重要的幾個模塊。

1.1 SqlSessionFactoryBuilder

從名稱長可以看出來使用的建造者設計模式(Builder),用於構建SqlSessionFactory對象

1.解析mybatis的xml配置文件,然后創建Configuration對象(對應<configuration>標簽);

2.根據創建的Configuration對象,創建SqlSessionFactory(默認使用DefaultSqlSessionFactory);

1.2 SqlSessionFactory

從名稱上可以看出使用的工廠模式(Factory),用於創建並初始化SqlSession對象(數據庫會話)

1.調用openSession方法,創建SqlSession對象,可以將SqlSession理解為數據庫連接(會話);

2.openSession方法有多個重載,可以創建SqlSession關閉自動提交、指定ExecutorType、指定數據庫事務隔離級別….

package org.apache.ibatis.session;

import java.sql.Connection;

public interface SqlSessionFactory {

    /**
     * 使用默認配置
     * 1.默認不開啟自動提交
     * 2.執行器Executor默認使用SIMPLE
     * 3.使用數據庫默認的事務隔離級別
     */

    SqlSession openSession();

    /**
     * 指定是否開啟自動提交
     * @param autoCommit 是否開啟自動提交
     */

    SqlSession openSession(boolean autoCommit);

    /**
     * 根據已有的數據庫連接創建會話(事務)
     * @param connection 數據庫連接
     */

    SqlSession openSession(Connection connection);

    /**
     * 創建連接時,指定數據庫事務隔離級別
     * @param level 事務隔離界別
     */

    SqlSession openSession(TransactionIsolationLevel level);

    /**
     * 創建連接時,指定執行器類型
     * @param execType 執行器
     */

    SqlSession openSession(ExecutorType execType);

    SqlSession openSession(ExecutorType execType, boolean autoCommit);

    SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

    SqlSession openSession(ExecutorType execType, Connection connection);

    /**
     * 獲取Configuration對象,也就是解析xml配置文件中的<configuration>標簽后的數據
     */

    Configuration getConfiguration();

}

1.3 SqlSession

如果了解web開發,就應該知道cookie和session吧,SqlSession的session和web開發中的session概念類似。

session,譯為“會話、會議”,數據的有效時間范圍是在會話期間(會議期間),會話(會議)結束后,數據就清除了。

也可以將SqlSession理解為一個數據庫連接(但也不完全正確,因為創建SqlSession之后,如果不執行sql,那么這個連接是無意義的,所以數據庫連接在執行sql的時候才建立的)。

SqlSession是一個接口,定義了很多操作數據庫的方法聲明:

public interface SqlSession extends Closeable {

    /* 獲取數據庫連接 */
    Connection getConnection();

    /* 數據庫的增刪改查 */

    <T> T selectOne(String statement);

    <E> List<E> selectList(String statement);

    int update(String statement, Object parameter);

    int delete(String statement, Object parameter);

    /* 事務回滾與提交 */

    void rollback();

    void commit();

    /* 清除一級緩存 */

    void clearCache();

    // 此處省略了很多方法

}

SqlSession只是定義了執行sql的一些方法,而具體的實現由子類來完成,比如SqlSession有一個接口實現類DefaultSqlSession。

MyBatis中通過Executor來執行sql的,在創建SqlSession的時候(openSession),同時會創建一個Executor對象,如下:

    @Override
    public SqlSession openSession() {
    
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    
    }
    
    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);
    
            // 利用傳入的參數,創建executor對象
            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();
    
        }
    
    }

    1.4 Executor

    Executor(人稱“執行器”)是一個接口,定義了對JDBC的封裝;

    MyBatis中提供了多種執行器,如下:

    image

    上面的圖中,雖然列出了5個Executor(BaseExecutor是抽象類),其實Executor只有三種:

      public enum ExecutorType {
      
          SIMPLE, // 簡單
      
          REUSE,  // 復用
      
          BATCH;  // 批量
      
      }

      CacheExecutor其實是一個Executor代理類,包含一個delegate,需要創建時手動傳入(要入simple、reuse、batch三者之一);

      ClosedExecutor,所有接口都會拋出異常,表示一個已經關閉的Executor;

      創建SqlSession時,默認使用的是SimpleExecutor;

      下面是創建Executor的代碼:org.apache.ibatis.session.Configuration#newExecutor()

      image

      上面說了,Executor是對JDBC的封裝。當我們使用JDBC來執行sql時,一般會先預處理sql,也就是conn.prepareStatement(sql),獲取返回的

      PreparedStatement對象(實現了Statement接口),再調用statement的executeXxx()來執行sql。

      也就是說,Executor在執行sql的時候也是需要創建Statement對象的,下面以SimpleExecutor為例

      image

      1.5 StatementHandler

      在JDBC中,是調用Statement.executeXxx()來執行sql;

      在MyBatis,也是調用Statement.executeXxx()來執行sql,此時就不得不提StatementHandler,可以將其理解為一個工人,他的工作包括

      1.對sql進行預處理;

      2.調用statement.executeXxx()執行sql;

      3.將數據庫返回的結果集進行對象轉換(ORM);

      public interface StatementHandler {
      
          /**
           * 獲取預處理對象
           */
          Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
      
          /**
           * 進行預處理
           */
          void parameterize(Statement statement) throws SQLException;
      
          /**
           * 批量sql(內部調用statement.addBatch)
           */
          void batch(Statement statement) throws SQLException;
      
          /**
           * 執行更新操作
           * @return 修改的記錄數
           */
          int update(Statement statement) throws SQLException;
      
          /**
           * 執行查詢操作
           * @param statement     sql生成的statement
           * @param resultHandler 結果集的處理邏輯
           * @return 查詢結果
           */
          <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
      
          <E> Cursor<E> queryCursor(Statement statement) throws SQLException;
      
          BoundSql getBoundSql();
      
          ParameterHandler getParameterHandler();
      
      }

      StatementHandler的相關子類如下圖所示:

      image

      以BaseStatementHandler為例:

      image

      1.6 ParameterHandler

      ParameterHandler的功能就是sql預處理后,進行設置參數:

      public interface ParameterHandler {
      
          Object getParameterObject();
      
          void setParameters(PreparedStatement ps) throws SQLException;
      }
      ParamterHandler有一個DefaultParameterHandler,下面是其重寫setParameters的代碼:

      image

      1.7 ResultSetHandler

      當執行statement.execute()后,就可以通過statement.getResultSet()來獲取結果集,

      獲取到結果集之后,MyBatis會使用ResultSetHandler來將結果集的數據轉換為Java對象(ORM映射)

      public interface ResultSetHandler {
      
          /**
           * 從statement中獲取結果集,並將結果集的數據庫屬性字段映射到Java對象屬性
           * @param stmt 已經execute的statement,調用state.getResultSet()獲取結果集
           * @return 轉換后的數據
           */
      
          <E> List<E> handleResultSets(Statement stmt) throws SQLException;
      
          <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
      
          void handleOutputParameters(CallableStatement cs) throws SQLException;
      
      }

      ResultSetHandler有一個實現類,DefaultResultHandler,其重寫handlerResultSets方法,如下:

      image

      1.8 TypeHandler

      TypeHandler主要用在兩個地方:

      1.參數綁定,發生在ParameterHandler.setParamenters()中。

      MyBatis中,可以使用<resultMap>來定義結果的映射關系,包括每一個字段的類型,比如下面這樣:

      <resultMap id="baseMap" type="cn.ganlixin.model.User">
          <id column="uid" property="id" jdbcType="INTEGER" />
          <result column="uname" property="name" jdbcType="VARCHAR" />
      </resultMap>
      TypeHandler,可以對某個字段按照xml中配置的類型進行設置值,比如設置sql的uid參數時,類型為INTEGER(jdbcType)。

      2.獲取結果集中的字段值,發生在ResultSetHandler處理結果集的過程中。

      TypeHandler的定義如下

      public interface TypeHandler<T> {
      
          /**
           * 設置預處理參數
           *
           * @param ps        預處理statement
           * @param i         參數位置
           * @param parameter 參數值
           * @param jdbcType  參數的jdbc類型
           */
          void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
      
          /**
           * 獲取一條結果集的某個字段值
           *
           * @param rs         一條結果集
           * @param columnName 列名(字段名稱)
           * @return 字段值
           */
          T getResult(ResultSet rs, String columnName) throws SQLException;
       
          /**
           * 獲取一條結果集的某個字段值(按照字段的順序獲取)
           *
           * @param rs          一條結果集
           * @param columnIndex 字段列的順序
           * @return 字段值
           */
          T getResult(ResultSet rs, int columnIndex) throws SQLException;
      
          T getResult(CallableStatement cs, int columnIndex) throws SQLException;
      }

      2.  總結

      把執行的流程總結一下:

      mybatis


      免責聲明!

      本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



       
      粵ICP備18138465號   © 2018-2025 CODEPRJ.COM