MyBatis源碼分析(二)prepareStatement預編譯的執行流程

通常我們如果自己寫建立數據庫連接的代碼的時候,都會這麼寫

pstmt = conn.prepareStatement(sql);
pstmt.setString(1, email);
result = pstmt.executeQuery();

而Mybatis是怎麼封裝,又是怎麼進行預編譯的呢,今天就一文讓你理解Mybatis的原理

一、獲取Executor

我們之前一篇文章《MyBatis源碼分析(一)基本請求流程》講了Mybatis執行的基本流程,包括Plugin插件。
其實在執行過程中,所有的sql語句都要經過執行器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;
  }

可以看到創建的執行器有三種,分別是

  • BatchExecutor:批量執行的執行器,官方說明這個executor是用於執行存儲過程的和批量操作的,因此這個方法是循環或者多次執行構建一個存儲過程或批處理過程。BatchExecutor 的事務是沒法自動提交的。因爲 BatchExecutor 只有在調用了 SqlSession 的 commit 方法的時候 , 它纔會去執行 executeBatch 方法。這個執行器基本用不到,現在的正常的公司都不允許搞存儲過程。
  • ReuseExecutor:會緩存創建的prepareStatement,但是隻能在同一個sqlSession中,用處不大,待會說prepareStatement還會再介紹這個執行器。
  • SimpleExecutor:是默認的執行器,這個執行器是在什麼地方指定的呢,在Configuration類中:
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

以上的三個Executor都有同一個父類:BaseExecutor,在BaseExecutor的query方法中,調用了三個子類的doQuery方法,update方法中,調用了三個子類的doUpdate方法,這是用了模板方法設計模式

二、SqlSession

SqlSession是一次數據庫連接,在Mybatis中,是通過如下方式創建的:
SqlSession創建
同時也可以看到,一個SqlSession對應創建一個新的執行器Executor。這也是爲什麼我們上面說ReuseExecutor沒有什麼用處的原因,在Mybatis中一個sql語句就會創建一個SqlSession,創建一個執行器Executor,ReuseExecutor就算緩存了PrepareStatement也用不上。

三、關閉連接

在執行sql的時候,會調用到SqlSessionTemplate,這是一個模板方法的類。在invoke方法執行完後,會調用finally方法關閉連接:

   } finally {
       if (sqlSession != null) {
           SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
       }

   }

這個方法中調用了session.close(),裏面又調用了executor.close()方法去關閉SqlSession,Executor從而關閉SqlSession,釋放連接。

四、SimpleExecutor

SimpleExecutor是Mybatis默認使用的執行器,其doQuery方法如下:

  @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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

可以看到裏面限制性的prepareStatement方法,然後又執行了query方法。
prepareStatement方法裏面其實就是封裝了我們手動創建PrepareStatement的時候的conn.prepareStatement方法,先去進行預編譯,預編譯完成後,在調用query方法執行。
由此可見Mybatis底層還是用的我們自己創建Statement並執行sql語句的方法。

五、SqlNode

在Mybatis中,我們都要寫xml文件,例如這樣一段xml寫的sql:

    <select id="selectByIdList" resultMap="BaseResultMap" parameterType="Long" >
        select
        <include refid="Base_Column_List" />
        from table_1
        where id in
        <foreach collection="list" item="status_id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

Mybatis是怎麼把這樣的語句解析成sql語句的呢,就是通過SqlNode,SqlNode是一個接口類,它有很多子類
SqlNode子類
例如IfSqlNode就是用來解析Mybatis文件下面的 <if 標籤,其入口就是在MappedStatement.getBoundSql方法中,經過一系列SqlNode解析後,就變成了一條正常的sql語句。

六、總結

本篇文章主要向你介紹了執行sql的一些關鍵節點:

  • Executor:執行有有BatchExecutor、SimpleExecutor、ReuseExecutor,這三個執行器默認的同時也是最常用的是SimpleExecutor
  • SqlSession: 在Mybatis中正常情況下執行sql的會話,sql執行完則關閉此會話,同時還會關閉對應的執行器。
  • SqlNode:在Mybatis中的xml語法,怎麼解析成Sql語句呢?就是通過SqlNode

在讀了這篇文章後,你是不是對Mybatis的原理理解地非常深入了呢?爲了讓你更好的進行開發工作,博主創建了一篇專欄教你精通使用在工作過程中的各種工具:《精通java開發工具》快來提升你的工作效率吧。

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