Mybatis源碼解析4——SqlSession


上一篇文章中,我們介紹了 SqlSessionFactory 的創建過程,忘記了的,可以回顧一下,或者看下下面這張圖也行。

image-20210722082105477

接下來,可樂講給大家介紹 Mybatis 中另一個重量級嘉賓——SqlSession,有了這個對象,我們就能對數據進行一頓操作了。大家伙小板凳搬起來,請看可樂為大家一一道來。

搬好小板凳

1、實例代碼

在實例搭建文章中,通過 SqlSession 對象查詢數據,可樂寫了兩種方法。

①、常規的需要我們拼接 statement 方式;

②、xxxMapper.interface 接口代理方式;

image-20210723075116982

對應下面兩種方法:

//根據id查詢person表數據
@Test
public void testSelectPersonById() {
    /*這個字符串由 PersonMapper.xml 文件中 兩個部分構成
        <mapper namespace="com.itcoke.mapper.PersonMapper"> 的 namespace 的值
        <select id="selectPersonById" > id 值
    */
    String namespace = "com.itcoke.mapper.PersonMapper";
    String method = "selectPersonById";
    //根據 sqlSessionFactory 產生 session
    SqlSession sqlSession = sessionFactory.openSession();
    Person person = sqlSession.selectOne(namespace + "." + method, 1L);
    System.out.println(person);
    sqlSession.close();
}


//根據id查詢person表數據
//通過接口代理的方式
@Test
public void testInterfaceSelectPersonById() {
    //根據 sqlSessionFactory 產生 session
    SqlSession sqlSession = sessionFactory.openSession();
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
    Person person = mapper.selectPersonById(1L);
    System.out.println(person);
    sqlSession.close();
}

本篇文章可樂講給大家介紹第一種原生方式,其實第二種接口代理最后也是走的第一種方式,這個我們下篇文章具體介紹。

2、構建過程圖示

image-20210815211833789

3、代碼剖析

3.1 Executor

我們通過 DefaultSessionFactory.openSession() 方法獲取 sqlSession

image-20210726222837613

其實是可以通過構造方法指定 Executor 的類型,比如:

SqlSession sqlSession = sessionFactory.openSession(ExecutorType.SIMPLE);

再看生成 Executor 的源代碼:

image-20210726223115962

如果不指定執行器類型,直接默認 openSession() 方法,生成的是 CachingExecutor 執行器,這里的 cacheEnabled 其實是默認開啟二級緩存的配置,在 mybatis-config.xml 文件中.

並且需要注意的是這里 new CachingExecutor(executor),傳進去了一個 SimpleExecutor 對象,后面和數據庫交互的實際上是該對象。

image-20210726224122488 image-20210726223429105

得到了 SqlSession,接下來看這段代碼:

Person person = sqlSession.selectOne(namespace + "." + method, 1L);

image-20210726224337803

直接看上面源碼的第 76 行代碼:

List<T> list = this.selectList(statement, parameter);

image-20210726224535733

在上一篇文章介紹 SqlSessionFactory 的構建過程時,我們說了 configuration 對象的組成:

image-20210722222701374

看上面的源碼得到 MappedStatement 對象,包含了我們在 mapper.xml 文件中配置的 sql 語句。

image-20210727073353475

執行 executor.query() 方法,注意,這里的 executor 是 CachingExecutor:

image-20210727073829137

這段源碼,我們可以得到兩個信息:

①、獲取我們指定配置的boundSql 對象,包含我們配置的 sql 語句和參數信息。

②、根據相關信息得到一個緩存 key,通過這個key,連續兩次相同的查詢,第二次可以不去查數據庫,直接獲取緩存的數據。

接着我們繼續看 query() 方法:

image-20210727074520738

看源碼,也就是說先去查緩存,緩存命中了直接返回數據,沒有命中就執行:delegate.query() 方法。

這里的 delegate 是上文我們說的構造 Executor 傳進來得 SimpleExecutor 對象。

image-20210727080139697

關於 mybatis 緩存后面可樂會專門寫一篇文章來仔細介紹,這里我們先梳理主線,看上面第 156 行代碼,緩存查不到,從數據庫里面查。

繼續跟 queryFromDatabase() 方法:

image-20210727080523287

3.2 StatementHandler

執行到上面第 61 行源碼:

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

跟進去:

image-20210727082254753

這里構造的是一個 RoutingStatementHandler 對象,聰明的你一聽聽名字都知道會路由生成別的對象。

沒錯,是根據傳入的 statementType 生成具體的對象:

image-20210727082515553

在 MappedStatement 對象中,默認 statementType 是 PERPARED:

image-20210727082829357

也就是這里最終生成的 StatementHandler 實際上是 PreparedStatementHandler 對象。

通常情況下:

①、不帶參數的 SQL語句執行,會生成 SimpleStatementHandler 對象。

②、帶參數的 SQL 語句執行,會生成 PreparedStatementHandler 對象。

③、存儲過程執行,會生成 CallableStatementHandler 對象。

3.3 ParameterHandler

再回到 SimpleExecutor 的 doQuery() 方法:

image-20210727080523287

看第 62 行代碼:

stmt = prepareStatement(handler, ms.getStatementLog());

跟進去:

image-20210729231618083

這里第 86 行獲取數據庫連接。

第 88 行,進行參數處理,說直接點,就是將 SQL 語句中的 “?” 替換成我們傳入的具體值。

但是我們知道 Java對象參數和數據庫參數是不一樣的,那么肯定還會做參數類型轉換,沒錯,就是通過下面將要介紹 TypeHandler 來完成。

3.4 TypeHandler

也就是將 Java 類型和 數據庫類型進行互相轉換。

image-20210729230021702

Mybatis 提供給了很多內置的參數轉換,基本上不需要我們自己去定義。

image-20210728221922904

當然,聰明的你可能會問了,假如這些都不滿足呢?假如我要自定義一些類型呢?

不用擔心,Mybatis 給我們預留了自定義類型的接口,如果你想自定義類型,通常分為兩步:

①、實現 org.apache.ibatis.type.TypeHandler 接口, 或繼承類 org.apache.ibatis.type.BaseTypeHandler

②、在配置文件中配置自定義的 TypeHnadler。

這里可樂就不做詳細描述了,官網寫的也很清楚:

https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers

3.4 ResultSetHandler

還是回到 SimpleExecutor 的 doQuery() 方法:

上一步我們得到了 PrepraedStatementHandler 對象,接着看 handler.query() 方法:

image-20210727083626964

通過執行 ps.execute() 方法,得到結果集,然后通過 resultSetHandler 去處理結果集。

4、總結

這樣,可樂就給大家完整的講解了如何通過 SqlSession 進行一次數據庫查詢操作,但是正如文章開頭所言,可樂給大家介紹了兩種查詢方式,一種是需要自己拼接 namespace+method 語句,另一種是通過 接口的形式。其實這兩種方式是一樣的,namespace 就是接口的包名(對應xxxMapper.xml 的namespace名稱),method 就是接口的方法名(對應到xxxMapper.xml 的每個SQL語句的 id 屬性),通過兩者拼接,我們能去 xxxMapper.xml 文件中定位到具體的 SQL 語句。

很明顯,通過接口的形式避免了我們寫字符串錯誤的可能,實際開發中,我們基本上都是這種方式,下一篇文章,可樂將給大家揭秘這種方式的奧秘。


免責聲明!

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



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