上一篇文章中,我們介紹了 SqlSessionFactory 的建立過程,忘記了的,可以回顧一下,或者看下下面這張圖也行。
接下來,可樂講給大家介紹 Mybatis 中另一個重量級嘉賓——SqlSession,有了這個物件,我們就能對資料進行一頓操作了。大傢伙小板凳搬起來,請看可樂為大家一一道來。
1、例項程式碼
在例項搭建文章中,通過 SqlSession 物件查詢資料,可樂寫了兩種方法。
①、常規的需要我們拼接 statement 方式;
②、xxxMapper.interface 介面代理方式;
對應下面兩種方法:
//根據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、構建過程圖示
3、程式碼剖析
3.1 Executor
我們通過 DefaultSessionFactory.openSession() 方法獲取 sqlSession
其實是可以通過構造方法指定 Executor 的型別,比如:
SqlSession sqlSession = sessionFactory.openSession(ExecutorType.SIMPLE);
再看生成 Executor 的原始碼:
如果不指定執行器型別,直接預設 openSession() 方法,生成的是 CachingExecutor 執行器,這裡的 cacheEnabled 其實是預設開啟二級快取的配置,在 mybatis-config.xml 檔案中.
並且需要注意的是這裡 new CachingExecutor(executor),傳進去了一個 SimpleExecutor 物件,後面和資料庫互動的實際上是該物件。
得到了 SqlSession,接下來看這段程式碼:
Person person = sqlSession.selectOne(namespace + "." + method, 1L);
直接看上面原始碼的第 76 行程式碼:
List<T> list = this.selectList(statement, parameter);
在上一篇文章介紹 SqlSessionFactory 的構建過程時,我們說了 configuration 物件的組成:
看上面的原始碼得到 MappedStatement 物件,包含了我們在 mapper.xml 檔案中配置的 sql 語句。
執行 executor.query() 方法,注意,這裡的 executor 是 CachingExecutor:
這段原始碼,我們可以得到兩個資訊:
①、獲取我們指定配置的boundSql 物件,包含我們配置的 sql 語句和引數資訊。
②、根據相關資訊得到一個快取 key,通過這個key,連續兩次相同的查詢,第二次可以不去查資料庫,直接獲取快取的資料。
接著我們繼續看 query() 方法:
看原始碼,也就是說先去查快取,快取命中了直接返回資料,沒有命中就執行:delegate.query() 方法。
這裡的 delegate 是上文我們說的構造 Executor 傳進來得 SimpleExecutor 物件。
關於 mybatis 快取後面可樂會專門寫一篇文章來仔細介紹,這裡我們先梳理主線,看上面第 156 行程式碼,快取查不到,從資料庫裡面查。
繼續跟 queryFromDatabase() 方法:
3.2 StatementHandler
執行到上面第 61 行原始碼:
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
跟進去:
這裡構造的是一個 RoutingStatementHandler 物件,聰明的你一聽聽名字都知道會路由生成別的物件。
沒錯,是根據傳入的 statementType 生成具體的物件:
在 MappedStatement 物件中,預設 statementType 是 PERPARED:
也就是這裡最終生成的 StatementHandler 實際上是 PreparedStatementHandler 物件。
通常情況下:
①、不帶引數的 SQL語句執行,會生成 SimpleStatementHandler 物件。
②、帶引數的 SQL 語句執行,會生成 PreparedStatementHandler 物件。
③、儲存過程執行,會生成 CallableStatementHandler 物件。
3.3 ParameterHandler
再回到 SimpleExecutor 的 doQuery() 方法:
看第 62 行程式碼:
stmt = prepareStatement(handler, ms.getStatementLog());
跟進去:
這裡第 86 行獲取資料庫連線。
第 88 行,進行引數處理,說直接點,就是將 SQL 語句中的 “?” 替換成我們傳入的具體值。
但是我們知道 Java物件引數和資料庫引數是不一樣的,那麼肯定還會做引數型別轉換,沒錯,就是通過下面將要介紹 TypeHandler 來完成。
3.4 TypeHandler
也就是將 Java 型別和 資料庫型別進行互相轉換。
Mybatis 提供給了很多內建的引數轉換,基本上不需要我們自己去定義。
當然,聰明的你可能會問了,假如這些都不滿足呢?假如我要自定義一些型別呢?
不用擔心,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() 方法:
通過執行 ps.execute() 方法,得到結果集,然後通過 resultSetHandler 去處理結果集。
4、總結
這樣,可樂就給大家完整的講解了如何通過 SqlSession 進行一次資料庫查詢操作,但是正如文章開頭所言,可樂給大家介紹了兩種查詢方式,一種是需要自己拼接 namespace+method 語句,另一種是通過 介面的形式。其實這兩種方式是一樣的,namespace 就是介面的包名(對應xxxMapper.xml 的namespace名稱),method 就是介面的方法名(對應到xxxMapper.xml 的每個SQL語句的 id 屬性),通過兩者拼接,我們能去 xxxMapper.xml 檔案中定位到具體的 SQL 語句。
很明顯,通過介面的形式避免了我們寫字串錯誤的可能,實際開發中,我們基本上都是這種方式,下一篇文章,可樂將給大家揭祕這種方式的奧祕。