MyBatis 源碼篇-SQL 執行的流程

本章通過一個簡單的例子,來了解 MyBatis 執行一條 SQL 語句的大致過程是怎樣的。

案例代碼如下所示:

public class MybatisTest {

    @Test
    public void selectByPrimaryKey() throws IOException {
        // 3
        StudentDao studentDao = getSqlSession().getMapper(StudentDao.class);
        // 4
        Student student = studentDao.selectByPrimaryKey(1L);
        System.out.println(student);
    }

    /**
     * 獲取SqlSession
     * 
     * @return
     */
    private SqlSession getSqlSession() throws IOException {
        // 1
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 2
        return sqlSessionFactory.openSession(true);
    }
}

Configuration

第一步,通過資源加載模塊加載配置文件,解析器模塊解析 XML 文件,生成 Configuration 對象。

源碼內容參考:org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) 方法

深入 parse() 方法可以查看配置文件的具體解析過程,以及如何生成 Configuration 對象。

Configuration 對象中緩存了 mybatis-config.xml 配置文件以及映射配置文件的所有內容。

SqlSession

第二步,通過 SqlSessionFactory 創建 SqlSession,SqlSession 是 MyBatis 暴露給外部使用的統一接口層,所有和數據庫打交道的操作都通過 SqlSession 這層。

下面通過時序圖描述 SqlSession 對象的創建流程:

MapperProxy

第三步,通過 SqlSession 獲得 StudentMapper 對象,爲了便於理解,下面通過時序圖描述 Mapper 對象的獲取流程。

MapperRegistry 是 Configuration 的一個屬性,MapperRegistry 緩存了 MapperProxyFactory 的 Map 集合,也就是說在解析完配置文件後,knownMappers 集合數據已經在 Configuration 對象中存在了。

 

問題一:爲什麼 StudentMapper(接口) 可以調用方法?

通過斷點調試來看看 StudentMapper 的真實對象是什麼:

MapperProxy 類的代碼如下:

MapperProxyFactory 的 newInstance 方法實現代碼如下:

通過上面的源碼內容,可以得出如下結論:

  • StudentMapper 的真實對象是 MapperProxy;
  • MapperProxy 繼承 InvocationHandler,實現 invoke 方法;
  • MapperProxyFactory 的 newInstance 方法,通過 JDK 動態代理的方式創建了一個 MapperProxy 的代理類;

MyBatis 的 Mapper 是通過動態代理實現的,調用 Mapper 的任何方法都會執行 MapperProxy 的 invoke 方法。

MyBatis 使用的動態代理和通常的動態代理有點區別,沒有實現類,只有接口。

動態代理類圖結構如下所示:

MyBatis 動態代理類圖結構如下所示:

selectByPrimaryKey

第四部,執行 selectByPrimaryKey 查詢學生信息。在第8行代碼上面打上斷點,調試選擇“step into”,進入子函數內部,調用 org.apache.ibatis.binding.MapperProxy#invoke 方法。

MapperMethod 通過如下方式創建。

  • mapperInterface:interface com.yjw.mybatis.dao.StudentMapper;
  • method:invoke 方法中傳進來的 method 類;
  • Configuration:MyBatis 的核心類,所有配置信息都存在該類中;

接着執行 mapperMethod.execute(sqlSession, args) 方法。

接下來還是走到 SqlSession 類中,調用 selectOne 方法。繼續往下執行,會執行 Executor 的 query 方法。

通過上面的分析,簡單總結一下,我們可以抽象出 MyBatis 在執行一條 SQL 查詢的過程中涉及到的主要類:Configuration、SqlSession、MapperProxy、Exector,根據這些類畫出如下 MyBatis 執行 SQL 的時序圖:

 

  • 第一步是獲取 MapperProxy 代理對象;
  • 第二步是根據獲取的代理對象,執行查詢操作;

Exector

SqlSession 中的 JDBC 操作部分最終都會委派給 Exector 實現,接着上面的斷點往下執行,進入 Exector 的 query 方法。

下面通過時序圖描述 Exector 的執行流程,真實的調用鏈路類比較多,這裏簡化了調用鏈路,省略了一些裝飾類、代理類,便於理解:

 

根據 Exector 執行的時序圖,可以抽象出的主要類是:SqlSession、Exector、StatementHandler、ParameterHandler、ResultSetHandler。

StatementHandler 接口是 MyBatis 的核心接口之一,它是 Exector 接口實現的基礎。StatementHandler 的主要功能很多,例如創建 Statement 對象,爲 SQL 語句綁定實參,執行 SQL 語句,將結果集映射成結果對象。

StatementHandler 類中包含了 ParameterHandler 和 ResultSetHandler 的屬性。ParameterHandler 的主要功能是爲 SQL 語句綁定實參,也就是使用傳入的參數替換 SQL 語句中的“?”佔位符。ResultSetHandler 的主要功能是將結果集映射成結果對象。

參考 BaseStatementHandler 類:org.apache.ibatis.executor.statement.BaseStatementHandler

結論

根據上面的分析,抽象出的主要類有:Configuration、SqlSession、MapperProxy、Exector、StatementHandler、ParameterHandler、ResultSetHandler。

 

MyBatis 源碼篇

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