免费在线a视频-免费在线观看a视频-免费在线观看大片影视大全-免费在线观看的视频-色播丁香-色播基地

面試官問(wèn)你MyBatis SQL是如何執(zhí)行的?把這篇文章甩給他

:2020年02月26日 腳本之家
分享到:

MyBatis 是第一個(gè)支持自定義 SQL、存儲(chǔ)過(guò)程和高級(jí)映射的類(lèi)持久框架。MyBatis 消除了大部分 JDBC 的樣板代碼、手動(dòng)設(shè)置參數(shù)以及檢索結(jié)果。MyBatis 能夠支持簡(jiǎn)單的 XML 和注解配置規(guī)則。使 Map 接口和 POJ...

初識(shí) MyBatis

MyBatis 是第一個(gè)支持自定義 SQL、存儲(chǔ)過(guò)程和高級(jí)映射的類(lèi)持久框架。MyBatis 消除了大部分 JDBC 的樣板代碼、手動(dòng)設(shè)置參數(shù)以及檢索結(jié)果。MyBatis 能夠支持簡(jiǎn)單的 XML 和注解配置規(guī)則。使 Map 接口和 POJO 類(lèi)映射到數(shù)據(jù)庫(kù)字段和記錄。

MyBatis 的特點(diǎn)

那么 MyBatis 具有什么特點(diǎn)呢?或許我們可以從如下幾個(gè)方面來(lái)描述

  • MyBatis 中的 SQL 語(yǔ)句和主要業(yè)務(wù)代碼分離,我們一般會(huì)把 MyBatis 中的 SQL 語(yǔ)句統(tǒng)一放在 XML 配置文件中,便于統(tǒng)一維護(hù)。

  • 解除 SQL 與程序代碼的耦合,通過(guò)提供 DAO 層,將業(yè)務(wù)邏輯和數(shù)據(jù)訪問(wèn)邏輯分離,使系統(tǒng)的設(shè)計(jì)更清晰,更易維護(hù),更易單元測(cè)試。SQL 和代碼的分離,提高了可維護(hù)性。

  • MyBatis 比較簡(jiǎn)單和輕量

本身就很小且簡(jiǎn)單。沒(méi)有任何第三方依賴(lài),只要通過(guò)配置 jar 包,或者如果你使用 Maven 項(xiàng)目的話(huà)只需要配置 Maven 以來(lái)就可以。易于使用,通過(guò)文檔和源代碼,可以比較完全的掌握它的設(shè)計(jì)思路和實(shí)現(xiàn)。

  • 屏蔽樣板代碼

MyBatis 回屏蔽原始的 JDBC 樣板代碼,讓你把更多的精力專(zhuān)注于 SQL 的書(shū)寫(xiě)和屬性-字段映射上。

  • 編寫(xiě)原生 SQL,支持多表關(guān)聯(lián)

MyBatis 最主要的特點(diǎn)就是你可以手動(dòng)編寫(xiě) SQL 語(yǔ)句,能夠支持多表關(guān)聯(lián)查詢(xún)。

  • 提供映射標(biāo)簽,支持對(duì)象與數(shù)據(jù)庫(kù)的 ORM 字段關(guān)系映射

  • ORM 是什么?對(duì)象關(guān)系映射(Object Relational Mapping,簡(jiǎn)稱(chēng)ORM) ,是通過(guò)使用描述對(duì)象和數(shù)據(jù)庫(kù)之間映射的元數(shù)據(jù),將面向?qū)ο笳Z(yǔ)言程序中的對(duì)象自動(dòng)持久化到關(guān)系數(shù)據(jù)庫(kù)中。本質(zhì)上就是將數(shù)據(jù)從一種形式轉(zhuǎn)換到另外一種形式。

  • 提供 XML 標(biāo)簽,支持編寫(xiě)動(dòng)態(tài) SQL。

   你可以使用 MyBatis XML 標(biāo)簽,起到 SQL 模版的效果,減少繁雜的 SQL 語(yǔ)句,便于維護(hù)。

MyBatis 整體架構(gòu)

MyBatis 最上面是接口層,接口層就是開(kāi)發(fā)人員在 Mapper 或者是 Dao 接口中的接口定義,是查詢(xún)、新增、更新還是刪除操作;

中間層是數(shù)據(jù)處理層,主要是配置 Mapper -> XML 層級(jí)之間的參數(shù)映射,SQL 解析,SQL 執(zhí)行,結(jié)果映射的過(guò)程。上述兩種流程都由基礎(chǔ)支持層來(lái)提供功能支撐,基礎(chǔ)支持層包括連接管理,事務(wù)管理,配置加載,緩存處理等。

接口層

在不與Spring 集成的情況下,使用 MyBatis 執(zhí)行數(shù)據(jù)庫(kù)的操作主要如下:

InputStream is = Resources.getResourceAsStream("myBatis-config.xml");

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

SqlSessionFactory factory = builder.build(is);

sqlSession = factory.openSession();

其中的SqlSessionFactory,SqlSession是 MyBatis 接口的核心類(lèi),尤其是 SqlSession,這個(gè)接口是MyBatis 中最重要的接口,這個(gè)接口能夠讓你執(zhí)行命令,獲取映射,管理事務(wù)。

數(shù)據(jù)處理層

  • 配置解析

在 Mybatis 初始化過(guò)程中,會(huì)加載 mybatis-config.xml 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息會(huì)形成相應(yīng)的對(duì)象并保存到 Configration 對(duì)象中。

之后,根據(jù)該對(duì)象創(chuàng)建SqlSessionFactory 對(duì)象。待 Mybatis 初始化完成后,可以通過(guò) SqlSessionFactory 創(chuàng)建 SqlSession 對(duì)象并開(kāi)始數(shù)據(jù)庫(kù)操作。

  • SQL 解析與 scripting 模塊

Mybatis 實(shí)現(xiàn)的動(dòng)態(tài) SQL 語(yǔ)句,幾乎可以編寫(xiě)出所有滿(mǎn)足需要的 SQL。

Mybatis 中 scripting 模塊會(huì)根據(jù)用戶(hù)傳入的參數(shù),解析映射文件中定義的動(dòng)態(tài) SQL 節(jié)點(diǎn),形成數(shù)據(jù)庫(kù)能執(zhí)行的SQL 語(yǔ)句。

  • SQL 執(zhí)行

SQL 語(yǔ)句的執(zhí)行涉及多個(gè)組件,包括 MyBatis 的四大核心,它們是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL 的執(zhí)行過(guò)程可以用下面這幅圖來(lái)表示

MyBatis 層級(jí)結(jié)構(gòu)各個(gè)組件的介紹(這里只是簡(jiǎn)單介紹,具體介紹在后面):

  • SqlSession:,它是 MyBatis 核心 API,主要用來(lái)執(zhí)行命令,獲取映射,管理事務(wù)。接收開(kāi)發(fā)人員提供 Statement Id 和參數(shù)。并返回操作結(jié)果。

  • Executor :執(zhí)行器,是 MyBatis 調(diào)度的核心,負(fù)責(zé) SQL 語(yǔ)句的生成以及查詢(xún)緩存的維護(hù)。

  • StatementHandler : 封裝了JDBC Statement 操作,負(fù)責(zé)對(duì) JDBC Statement 的操作,如設(shè)置參數(shù)、將Statement 結(jié)果集轉(zhuǎn)換成 List 集合。

  • ParameterHandler : 負(fù)責(zé)對(duì)用戶(hù)傳遞的參數(shù)轉(zhuǎn)換成 JDBC Statement 所需要的參數(shù)。

  • ResultSetHandler : 負(fù)責(zé)將 JDBC 返回的 ResultSet 結(jié)果集對(duì)象轉(zhuǎn)換成 List 類(lèi)型的集合。

  • TypeHandler : 用于 Java 類(lèi)型和 JDBC 類(lèi)型之間的轉(zhuǎn)換。

  • MappedStatement : 動(dòng)態(tài) SQL 的封裝

  • SqlSource : 表示從 XML 文件或注釋讀取的映射語(yǔ)句的內(nèi)容,它創(chuàng)建將從用戶(hù)接收的輸入?yún)?shù)傳遞給數(shù)據(jù)庫(kù)的 SQL。

  • Configuration: MyBatis 所有的配置信息都維持在 Configuration 對(duì)象之中。

基礎(chǔ)支持層

  • 反射模塊

Mybatis 中的反射模塊,對(duì) Java 反射進(jìn)行了很好的封裝,提供了簡(jiǎn)易的 API,方便上層調(diào)用,并且對(duì)反射操作進(jìn)行了一系列的優(yōu)化,比如,緩存了類(lèi)的 元數(shù)據(jù)(MetaClass)和對(duì)象的元數(shù)據(jù)(MetaObject),提高了反射操作的性能。

  • 類(lèi)型轉(zhuǎn)換模塊

Mybatis 的別名機(jī)制,能夠簡(jiǎn)化配置文件,該機(jī)制是類(lèi)型轉(zhuǎn)換模塊的主要功能之一。類(lèi)型轉(zhuǎn)換模塊的另一個(gè)功能是實(shí)現(xiàn) JDBC 類(lèi)型與 Java 類(lèi)型的轉(zhuǎn)換

在 SQL 語(yǔ)句綁定參數(shù)時(shí),會(huì)將數(shù)據(jù)由 Java 類(lèi)型轉(zhuǎn)換成 JDBC 類(lèi)型;在映射結(jié)果集時(shí),會(huì)將數(shù)據(jù)由 JDBC 類(lèi)型轉(zhuǎn)換成 Java 類(lèi)型。

  • 日志模塊

在 Java 中,有很多優(yōu)秀的日志框架,如 Log4j、Log4j2、slf4j 等。Mybatis 除了提供了詳細(xì)的日志輸出信息,還能夠集成多種日志框架,其日志模塊的主要功能就是集成第三方日志框架。

  • 資源加載模塊

該模塊主要封裝了類(lèi)加載器,確定了類(lèi)加載器的使用順序,并提供了加載類(lèi)文件和其它資源文件的功能。

  • 解析器模塊

該模塊有兩個(gè)主要功能:一個(gè)是封裝了 XPath,為 Mybatis 初始化時(shí)解析 mybatis-config.xml配置文件以及映射配置文件提供支持;另一個(gè)為處理動(dòng)態(tài) SQL 語(yǔ)句中的占位符提供支持。

  • 數(shù)據(jù)源模塊

Mybatis 自身提供了相應(yīng)的數(shù)據(jù)源實(shí)現(xiàn),也提供了與第三方數(shù)據(jù)源集成的接口。

數(shù)據(jù)源是開(kāi)發(fā)中的常用組件之一,很多開(kāi)源的數(shù)據(jù)源都提供了豐富的功能,如連接池、檢測(cè)連接狀態(tài)等,選擇性能優(yōu)秀的數(shù)據(jù)源組件,對(duì)于提供ORM 框架以及整個(gè)應(yīng)用的性能都是非常重要的。

  • 事務(wù)管理模塊

一般地,Mybatis 與 Spring 框架集成,由 Spring 框架管理事務(wù)。但 Mybatis 自身對(duì)數(shù)據(jù)庫(kù)事務(wù)進(jìn)行了抽象,提供了相應(yīng)的事務(wù)接口和簡(jiǎn)單實(shí)現(xiàn)。

  • 緩存模塊

Mybatis 中有一級(jí)緩存和二級(jí)緩存,這兩級(jí)緩存都依賴(lài)于緩存模塊中的實(shí)現(xiàn)。但是需要注意,這兩級(jí)緩存與Mybatis 以及整個(gè)應(yīng)用是運(yùn)行在同一個(gè) JVM 中的,共享同一塊內(nèi)存,如果這兩級(jí)緩存中的數(shù)據(jù)量較大,則可能影響系統(tǒng)中其它功能,所以需要緩存大量數(shù)據(jù)時(shí),優(yōu)先考慮使用 Redis、Memcache 等緩存產(chǎn)品。

  • Binding 模塊

在調(diào)用 SqlSession 相應(yīng)方法執(zhí)行數(shù)據(jù)庫(kù)操作時(shí),需要制定映射文件中定義的 SQL 節(jié)點(diǎn),如果 SQL 中出現(xiàn)了拼寫(xiě)錯(cuò)誤,那就只能在運(yùn)行時(shí)才能發(fā)現(xiàn)。

為了能盡早發(fā)現(xiàn)這種錯(cuò)誤,Mybatis 通過(guò) Binding 模塊將用戶(hù)自定義的Mapper 接口與映射文件關(guān)聯(lián)起來(lái),系統(tǒng)可以通過(guò)調(diào)用自定義 Mapper 接口中的方法執(zhí)行相應(yīng)的 SQL 語(yǔ)句完成數(shù)據(jù)庫(kù)操作,從而避免上述問(wèn)題。

注意,在開(kāi)發(fā)中,我們只是創(chuàng)建了 Mapper 接口,而并沒(méi)有編寫(xiě)實(shí)現(xiàn)類(lèi),這是因?yàn)?Mybatis 自動(dòng)為 Mapper 接口創(chuàng)建了動(dòng)態(tài)代理對(duì)象。

MyBatis 核心組件

在認(rèn)識(shí)了 MyBatis 并了解其基礎(chǔ)架構(gòu)之后,下面我們來(lái)看一下 MyBatis 的核心組件,就是這些組件實(shí)現(xiàn)了從 SQL 語(yǔ)句到映射到 JDBC 再到數(shù)據(jù)庫(kù)字段之間的轉(zhuǎn)換,執(zhí)行 SQL 語(yǔ)句并輸出結(jié)果集。首先來(lái)認(rèn)識(shí) MyBatis 的第一個(gè)核心組件

SqlSessionFactory

對(duì)于任何框架而言,在使用該框架之前都要經(jīng)歷過(guò)一系列的初始化流程,MyBatis 也不例外。MyBatis 的初始化流程如下

String resource = "org/mybatis/example/mybatis-config.xml";

InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

sqlSessionFactory.openSession();

上述流程中比較重要的一個(gè)對(duì)象就是SqlSessionFactory,SqlSessionFactory 是 MyBatis 框架中的一個(gè)接口,它主要負(fù)責(zé)的是

  • MyBatis 框架初始化操作

  • 為開(kāi)發(fā)人員提供SqlSession 對(duì)象

SqlSessionFactory 有兩個(gè)實(shí)現(xiàn)類(lèi),一個(gè)是 SqlSessionManager 類(lèi),一個(gè)是 DefaultSqlSessionFactory 類(lèi)

  • DefaultSqlSessionFactory : SqlSessionFactory 的默認(rèn)實(shí)現(xiàn)類(lèi),是真正生產(chǎn)會(huì)話(huà)的工廠類(lèi),這個(gè)類(lèi)的實(shí)例的生命周期是全局的,它只會(huì)在首次調(diào)用時(shí)生成一個(gè)實(shí)例(單例模式),就一直存在直到服務(wù)器關(guān)閉。

  • SqlSessionManager :已被廢棄,原因大概是: SqlSessionManager 中需要維護(hù)一個(gè)自己的線(xiàn)程池,而使用MyBatis 更多的是要與 Spring 進(jìn)行集成,并不會(huì)單獨(dú)使用,所以維護(hù)自己的 ThreadLocal 并沒(méi)有什么意義,所以 SqlSessionManager 已經(jīng)不再使用。

SqlSessionFactory 的執(zhí)行流程

下面來(lái)對(duì) SqlSessionFactory 的執(zhí)行流程來(lái)做一個(gè)分析

首先第一步是 SqlSessionFactory 的創(chuàng)建

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

從這行代碼入手,首先創(chuàng)建了一個(gè) SqlSessionFactoryBuilder 工廠,這是一個(gè)建造者模式的設(shè)計(jì)思想,由 builder 建造者來(lái)創(chuàng)建 SqlSessionFactory 工廠

然后調(diào)用 SqlSessionFactoryBuilder 中的 build 方法傳遞一個(gè)InputStream 輸入流,Inputstream 輸入流中就是你傳過(guò)來(lái)的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根據(jù)傳入的 InputStream 輸入流和environment、properties屬性創(chuàng)建一個(gè)XMLConfigBuilder對(duì)象。SqlSessionFactoryBuilder 對(duì)象調(diào)用XMLConfigBuilder 的parse()方法,流程如下。

XMLConfigBuilder 會(huì)解析/configuration標(biāo)簽,configuration 是 MyBatis 中最重要的一個(gè)標(biāo)簽,下面流程會(huì)介紹 Configuration 標(biāo)簽。

在 parseConfiguration 方法中,會(huì)對(duì)各個(gè)在 /configuration 中的標(biāo)簽進(jìn)行解析

重要配置

說(shuō)一下這些標(biāo)簽都是什么意思吧

  • properties,外部屬性,這些屬性都是可外部配置且可動(dòng)態(tài)替換的,既可以在典型的 Java 屬性文件中配置,亦可通過(guò) properties 元素的子元素來(lái)傳遞。

<properties>

<property name="driver" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost:3306/test" />

<property name="username" value="root" />

<property name="password" value="root" />

</properties>

一般用來(lái)給 environment 標(biāo)簽中的 dataSource 賦值

<environment id="development">

<transactionManager type="JDBC" />

<dataSource type="POOLED">

<property name="driver" value="${driver}" />

<property name="url" value="${url}" />

<property name="username" value="${username}" />

<property name="password" value="${password}" />

</dataSource>

</environment>

還可以通過(guò)外部屬性進(jìn)行配置,但是我們這篇文章以原理為主,不會(huì)介紹太多應(yīng)用層面的操作。

  • settings ,MyBatis 中極其重要的配置,它們會(huì)改變 MyBatis 的運(yùn)行時(shí)行為。

settings 中配置有很多,具體可以參考 https://mybatis.org/mybatis-3/zh/configuration.html#settings 詳細(xì)了解。這里介紹幾個(gè)平常使用過(guò)程中比較重要的配置

<settings>

<setting name="cacheEnabled" value="true"/>

<setting name="lazyLoadingEnabled" value="true"/>

</settings>

  • typeAliases,類(lèi)型別名,類(lèi)型別名是為 Java 類(lèi)型設(shè)置的一個(gè)名字。它只和 XML 配置有關(guān)。

<typeAliases>

<typeAlias alias="Blog" type="domain.blog.Blog"/>

</typeAliases>

當(dāng)這樣配置時(shí),Blog 可以用在任何使用 domain.blog.Blog 的地方。

  • typeHandlers,類(lèi)型處理器,無(wú)論是 MyBatis 在預(yù)處理語(yǔ)句(PreparedStatement)中設(shè)置一個(gè)參數(shù)時(shí),還是從結(jié)果集中取出一個(gè)值時(shí), 都會(huì)用類(lèi)型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類(lèi)型。

在 org.apache.ibatis.type 包下有很多已經(jīng)實(shí)現(xiàn)好的 TypeHandler,可以參考如下

你可以重寫(xiě)類(lèi)型處理器或創(chuàng)建你自己的類(lèi)型處理器來(lái)處理不支持的或非標(biāo)準(zhǔn)的類(lèi)型。

具體做法為:實(shí)現(xiàn) org.apache.ibatis.type.TypeHandler 接口, 或繼承一個(gè)很方便的類(lèi) org.apache.ibatis.type.BaseTypeHandler, 然后可以選擇性地將它映射到一個(gè) JDBC 類(lèi)型。

  • objectFactory,對(duì)象工廠,MyBatis 每次創(chuàng)建結(jié)果對(duì)象的新實(shí)例時(shí),它都會(huì)使用一個(gè)對(duì)象工廠(ObjectFactory)實(shí)例來(lái)完成。默認(rèn)的對(duì)象工廠需要做的僅僅是實(shí)例化目標(biāo)類(lèi),要么通過(guò)默認(rèn)構(gòu)造方法,要么在參數(shù)映射存在的時(shí)候通過(guò)參數(shù)構(gòu)造方法來(lái)實(shí)例化。如果想覆蓋對(duì)象工廠的默認(rèn)行為,則可以通過(guò)創(chuàng)建自己的對(duì)象工廠來(lái)實(shí)現(xiàn)。

public class ExampleObjectFactory extends DefaultObjectFactory {

public Object create(Class type) {

return super.create(type);

}

public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {

return super.create(type, constructorArgTypes, constructorArgs);

}

public void setProperties(Properties properties) {

super.setProperties(properties);

}

public <T> boolean isCollection(Class<T> type) {

return Collection.class.isAssignableFrom(type);

}

}

然后需要在 XML 中配置此對(duì)象工廠

<objectFactory type="org.mybatis.example.ExampleObjectFactory">

<property name="someProperty" value="100"/>

</objectFactory>

  • plugins,插件開(kāi)發(fā),插件開(kāi)發(fā)是 MyBatis 設(shè)計(jì)人員給開(kāi)發(fā)人員留給自行開(kāi)發(fā)的接口,MyBatis 允許你在已映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。

  • MyBatis 允許使用插件來(lái)攔截的方法調(diào)用包括:Executor、ParameterHandler、ResultSetHandler、StatementHandler 接口,這幾個(gè)接口也是 MyBatis 中非常重要的接口,我們下面會(huì)詳細(xì)介紹這幾個(gè)接口。

  • environments,MyBatis 環(huán)境配置,MyBatis 可以配置成適應(yīng)多種環(huán)境,這種機(jī)制有助于將 SQL 映射應(yīng)用于多種數(shù)據(jù)庫(kù)之中。例如,開(kāi)發(fā)、測(cè)試和生產(chǎn)環(huán)境需要有不同的配置;或者想在具有相同 Schema 的多個(gè)生產(chǎn)數(shù)據(jù)庫(kù)中 使用相同的 SQL 映射。

  • 這里注意一點(diǎn),雖然 environments 可以指定多個(gè)環(huán)境,但是 SqlSessionFactory 只能有一個(gè),為了指定創(chuàng)建哪種環(huán)境,只要將它作為可選的參數(shù)傳遞給 SqlSessionFactoryBuilder 即可。

  • SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);

  • SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

  • 環(huán)境配置如下

  • <environments default="development">

  • <environment id="development">

  • <transactionManager type="JDBC">

  • <property name="..." value="..."/>

  • </transactionManager>

  • <dataSource type="POOLED">

  • <property name="driver" value="${driver}"/>

  • <property name="url" value="${url}"/>

  • <property name="username" value="${username}"/>

  • <property name="password" value="${password}"/>

  • </dataSource>

  • </environment>

  • </environments>

  • databaseIdProvider ,數(shù)據(jù)庫(kù)廠商標(biāo)示,MyBatis 可以根據(jù)不同的數(shù)據(jù)庫(kù)廠商執(zhí)行不同的語(yǔ)句,這種多廠商的支持是基于映射語(yǔ)句中的 databaseId 屬性。

  • <databaseIdProvider type="DB_VENDOR">

  • <property name="SQL Server" value="sqlserver"/>

  • <property name="DB2" value="db2"/>

  • <property name="Oracle" value="oracle" />

  • </databaseIdProvider>

  • mappers,映射器,這是告訴 MyBatis 去哪里找到這些 SQL 語(yǔ)句,mappers 映射配置有四種方式

  • <!-- 使用相對(duì)于類(lèi)路徑的資源引用 -->

  • <mappers>

  • <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>

  • <mapper resource="org/mybatis/builder/BlogMapper.xml"/>

  • <mapper resource="org/mybatis/builder/PostMapper.xml"/>

  • </mappers><!-- 使用完全限定資源定位符(URL) -->

  • <mappers>

  • <mapper url="file:///var/mappers/AuthorMapper.xml"/>

  • <mapper url="file:///var/mappers/BlogMapper.xml"/>

  • <mapper url="file:///var/mappers/PostMapper.xml"/>

  • </mappers>

  • <!-- 使用映射器接口實(shí)現(xiàn)類(lèi)的完全限定類(lèi)名 -->

  • <mappers>

  • <mapper class="org.mybatis.builder.AuthorMapper"/>

  • <mapper class="org.mybatis.builder.BlogMapper"/>

  • <mapper class="org.mybatis.builder.PostMapper"/>

  • </mappers>

  • <!-- 將包內(nèi)的映射器接口實(shí)現(xiàn)全部注冊(cè)為映射器 -->

  • <mappers>

  • <package name="org.mybatis.builder"/>

  • </mappers>

上面的一個(gè)個(gè)屬性都對(duì)應(yīng)著一個(gè)解析方法,都是使用 XPath 把標(biāo)簽進(jìn)行解析,解析完成后返回一個(gè) DefaultSqlSessionFactory 對(duì)象,它是 SqlSessionFactory 的默認(rèn)實(shí)現(xiàn)類(lèi)。

這就是 SqlSessionFactoryBuilder 的初始化流程,通過(guò)流程我們可以看到,初始化流程就是對(duì)一個(gè)個(gè) /configuration 標(biāo)簽下子標(biāo)簽的解析過(guò)程。

SqlSession

在 MyBatis 初始化流程結(jié)束,也就是 SqlSessionFactoryBuilder -> SqlSessionFactory 的獲取流程后,我們就可以通過(guò) SqlSessionFactory 對(duì)象得到 SqlSession 然后執(zhí)行 SQL 語(yǔ)句了。具體來(lái)看一下這個(gè)過(guò)程

在 SqlSessionFactory.openSession 過(guò)程中我們可以看到,會(huì)調(diào)用到 DefaultSqlSessionFactory 中的 openSessionFromDataSource 方法,這個(gè)方法主要?jiǎng)?chuàng)建了兩個(gè)與我們分析執(zhí)行流程重要的對(duì)象,一個(gè)是 Executor 執(zhí)行器對(duì)象,一個(gè)是 SqlSession 對(duì)象。執(zhí)行器我們下面會(huì)說(shuō),現(xiàn)在來(lái)說(shuō)一下 SqlSession 對(duì)象

SqlSession 對(duì)象是 MyBatis 中最重要的一個(gè)對(duì)象,這個(gè)接口能夠讓你執(zhí)行命令,獲取映射,管理事務(wù)。

SqlSession 中定義了一系列模版方法,讓你能夠執(zhí)行簡(jiǎn)單的 CRUD 操作,也可以通過(guò) getMapper 獲取 Mapper 層,執(zhí)行自定義 SQL 語(yǔ)句,因?yàn)?SqlSession 在執(zhí)行 SQL 語(yǔ)句之前是需要先開(kāi)啟一個(gè)會(huì)話(huà),涉及到事務(wù)操作,所以還會(huì)有 commit、 rollback、close 等方法。這也是模版設(shè)計(jì)模式的一種應(yīng)用。

MapperProxy

MapperProxy 是 Mapper 映射 SQL 語(yǔ)句的關(guān)鍵對(duì)象,我們寫(xiě)的 Dao 層或者 Mapper 層都是通過(guò) MapperProxy 來(lái)和對(duì)應(yīng)的 SQL 語(yǔ)句進(jìn)行綁定的。下面我們就來(lái)解釋一下綁定過(guò)程

這就是 MyBatis 的核心綁定流程,我們可以看到 SqlSession 首先調(diào)用 getMapper 方法,我們剛才說(shuō)到 SqlSession 是大哥級(jí)別的人物,只定義標(biāo)準(zhǔn)(有一句話(huà)是怎么說(shuō)的來(lái)著,一流的企業(yè)做標(biāo)準(zhǔn),二流的企業(yè)做品牌,三流的企業(yè)做產(chǎn)品)。

SqlSession 不愿意做的事情交給 Configuration 這個(gè)手下去做,但是 Configuration 也是有小弟的,它不愿意做的事情直接甩給小弟去做,這個(gè)小弟是誰(shuí)呢?它就是 MapperRegistry,馬上就到核心部分了。

MapperRegistry 相當(dāng)于項(xiàng)目經(jīng)理,項(xiàng)目經(jīng)理只從大面上把握項(xiàng)目進(jìn)度,不需要知道手下的小弟是如何工作的,把任務(wù)完成了就好。最終真正干活的還是 MapperProxyFactory。

也就是說(shuō),MyBatis 中 Mapper 和 SQL 語(yǔ)句的綁定正是通過(guò)動(dòng)態(tài)代理來(lái)完成的。

通過(guò)動(dòng)態(tài)代理,我們就可以方便的在 Dao 層或者 Mapper 層定義接口,實(shí)現(xiàn)自定義的增刪改查操作了。那么具體的執(zhí)行過(guò)程是怎么樣呢?上面只是綁定過(guò)程,別著急,下面就來(lái)探討一下 SQL 語(yǔ)句的執(zhí)行過(guò)程。

有一部分代碼被遮擋,代碼有些多,不過(guò)不影響我們看主要流程

MapperProxyFactory 會(huì)生成代理對(duì)象,這個(gè)對(duì)象就是 MapperProxy,最終會(huì)調(diào)用到 mapperMethod.execute 方法,execute 方法比較長(zhǎng),其實(shí)邏輯比較簡(jiǎn)單,就是判斷是 插入、更新、刪除 還是 查詢(xún) 語(yǔ)句,其中如果是查詢(xún)的話(huà),還會(huì)判斷返回值的類(lèi)型,我們可以點(diǎn)進(jìn)去看一下都是怎么設(shè)計(jì)的。

很多代碼其實(shí)可以忽略,只看我標(biāo)出來(lái)的重點(diǎn)就好了,我們可以看到,不管你前面經(jīng)過(guò)多少道關(guān)卡處理,最終都逃不過(guò) SqlSession 這個(gè)老大制定的標(biāo)準(zhǔn)。

我們以 selectList 為例,來(lái)看一下下面的執(zhí)行過(guò)程。

這是 DefaultSqlSession 中 selectList 的代碼,我們可以看到出現(xiàn)了 executor,這是什么呢?我們下面來(lái)解釋。

Executor

還記得我們之前的流程中提到了 Executor(執(zhí)行器) 這個(gè)概念嗎?我們來(lái)回顧一下它第一次出現(xiàn)的位置。

由 Configuration 對(duì)象創(chuàng)建了一個(gè) Executor 對(duì)象,這個(gè) Executor 是干嘛的呢?下面我們就來(lái)認(rèn)識(shí)一下

Executor 的繼承結(jié)構(gòu)

每一個(gè) SqlSession 都會(huì)擁有一個(gè) Executor 對(duì)象,這個(gè)對(duì)象負(fù)責(zé)增刪改查的具體操作,我們可以簡(jiǎn)單的將它理解為 JDBC 中 Statement 的封裝版。

也可以理解為 SQL 的執(zhí)行引擎,要干活總得有一個(gè)發(fā)起人吧,可以把 Executor 理解為發(fā)起人的角色。

首先先從 Executor 的繼承體系來(lái)認(rèn)識(shí)一下

如上圖所示,位于繼承體系最頂層的是 Executor 執(zhí)行器,它有兩個(gè)實(shí)現(xiàn)類(lèi),分別是BaseExecutor和 CachingExecutor。

BaseExecutor 是一個(gè)抽象類(lèi),這種通過(guò)抽象的實(shí)現(xiàn)接口的方式是適配器設(shè)計(jì)模式之接口適配 的體現(xiàn),是Executor 的默認(rèn)實(shí)現(xiàn),實(shí)現(xiàn)了大部分 Executor 接口定義的功能,降低了接口實(shí)現(xiàn)的難度。

BaseExecutor 的子類(lèi)有三個(gè),分別是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。

SimpleExecutor : 簡(jiǎn)單執(zhí)行器,是 MyBatis 中默認(rèn)使用的執(zhí)行器,每執(zhí)行一次 update 或 select,就開(kāi)啟一個(gè)Statement 對(duì)象,用完就直接關(guān)閉 Statement 對(duì)象(可以是 Statement 或者是 PreparedStatment 對(duì)象)

ReuseExecutor : 可重用執(zhí)行器,這里的重用指的是重復(fù)使用 Statement,它會(huì)在內(nèi)部使用一個(gè) Map 把創(chuàng)建的Statement 都緩存起來(lái),每次執(zhí)行 SQL 命令的時(shí)候,都會(huì)去判斷是否存在基于該 SQL 的 Statement 對(duì)象,如果存在 Statement 對(duì)象并且對(duì)應(yīng)的 connection 還沒(méi)有關(guān)閉的情況下就繼續(xù)使用之前的 Statement 對(duì)象,并將其緩存起來(lái)。

因?yàn)槊恳粋€(gè) SqlSession 都有一個(gè)新的 Executor 對(duì)象,所以我們緩存在 ReuseExecutor 上的 Statement作用域是同一個(gè) SqlSession。

BatchExecutor : 批處理執(zhí)行器,用于將多個(gè) SQL 一次性輸出到數(shù)據(jù)庫(kù)

CachingExecutor: 緩存執(zhí)行器,先從緩存中查詢(xún)結(jié)果,如果存在就返回之前的結(jié)果;如果不存在,再委托給Executor delegate 去數(shù)據(jù)庫(kù)中取,delegate 可以是上面任何一個(gè)執(zhí)行器。

Executor 的創(chuàng)建和選擇

我們上面提到 Executor 是由 Configuration 創(chuàng)建的,Configuration 會(huì)根據(jù)執(zhí)行器的類(lèi)型創(chuàng)建,如下

這一步就是執(zhí)行器的創(chuàng)建過(guò)程,根據(jù)傳入的 ExecutorType 類(lèi)型來(lái)判斷是哪種執(zhí)行器,如果不指定 ExecutorType ,默認(rèn)創(chuàng)建的是簡(jiǎn)單執(zhí)行器。它的賦值可以通過(guò)兩個(gè)地方進(jìn)行賦值:

  • 可以通過(guò)<settings>標(biāo)簽來(lái)設(shè)置當(dāng)前工程中所有的 SqlSession 對(duì)象使用默認(rèn)的 Executor

<settings>

<!--取值范圍 SIMPLE, REUSE, BATCH -->

<setting name="defaultExecutorType" value="SIMPLE"/>

</settings>

  • 另外一種直接通過(guò)Java對(duì)方法賦值的方式

session = factory.openSession(ExecutorType.BATCH);

Executor 的具體執(zhí)行過(guò)程

Executor 中的大部分方法的調(diào)用鏈其實(shí)是差不多的,下面是深入源碼分析執(zhí)行過(guò)程,如果你沒(méi)有時(shí)間或者暫時(shí)不想深入研究的話(huà),給你下面的執(zhí)行流程圖作為參考。

我們緊跟著上面的 selectList 繼續(xù)分析,它會(huì)調(diào)用到 executor.query 方法。

當(dāng)有一個(gè)查詢(xún)請(qǐng)求訪問(wèn)的時(shí)候,首先會(huì)經(jīng)過(guò) Executor 的實(shí)現(xiàn)類(lèi) CachingExecutor ,先從緩存中查詢(xún) SQL 是否是第一次執(zhí)行,如果是第一次執(zhí)行的話(huà),那么就直接執(zhí)行 SQL 語(yǔ)句,并創(chuàng)建緩存,如果第二次訪問(wèn)相同的 SQL 語(yǔ)句的話(huà),那么就會(huì)直接從緩存中提取。

上面這段代碼是從 selectList -> 從緩存中 query 的具體過(guò)程。可能你看到這里有些覺(jué)得類(lèi)都是什么東西,我想鼓勵(lì)你一下,把握重點(diǎn),不用每段代碼都看,從找到 SQL 的調(diào)用鏈路,其他代碼想看的時(shí)候在看,看源碼就是很容易發(fā)蒙,容易煩躁,但是切記一點(diǎn),把握重點(diǎn)。

上面代碼會(huì)判斷緩存中是否有這條 SQL 語(yǔ)句的執(zhí)行結(jié)果,如果沒(méi)有的話(huà),就再重新創(chuàng)建 Executor 執(zhí)行器執(zhí)行 SQL 語(yǔ)句,注意, list = doQuery 是真正執(zhí)行 SQL 語(yǔ)句的過(guò)程,這個(gè)過(guò)程中會(huì)創(chuàng)建我們上面提到的三種執(zhí)行器,這里我們使用的是簡(jiǎn)單執(zhí)行器。

到這里,執(zhí)行器所做的工作就完事了,Executor 會(huì)把后續(xù)的工作交給 StatementHandler 繼續(xù)執(zhí)行。下面我們來(lái)認(rèn)識(shí)一下 StatementHandler

StatementHandler

StatementHandler 是四大組件中最重要的一個(gè)對(duì)象,負(fù)責(zé)操作 Statement 對(duì)象與數(shù)據(jù)庫(kù)進(jìn)行交互,在工作時(shí)還會(huì)使用 ParameterHandler 和 ResultSetHandler對(duì)參數(shù)進(jìn)行映射,對(duì)結(jié)果進(jìn)行實(shí)體類(lèi)的綁定,這兩個(gè)組件我們后面說(shuō)。

我們?cè)诖罱ㄔ?JDBC 的時(shí)候,會(huì)有這樣一行代碼

Statement stmt = conn.createStatement(); //也可以使用PreparedStatement來(lái)做

這行代碼創(chuàng)建的 Statement 對(duì)象或者是 PreparedStatement 對(duì)象就是由 StatementHandler 進(jìn)行管理的。

StatementHandler 的繼承結(jié)構(gòu)

有沒(méi)有感覺(jué)和 Executor 的繼承體系很相似呢?最頂級(jí)接口是四大組件對(duì)象,分別有兩個(gè)實(shí)現(xiàn)類(lèi) BaseStatementHandler 和 RoutingStatementHandler,BaseStatementHandler 有三個(gè)實(shí)現(xiàn)類(lèi), 他們分別是 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。

RoutingStatementHandler : RoutingStatementHandler 并沒(méi)有對(duì) Statement 對(duì)象進(jìn)行使用,只是根據(jù)StatementType 來(lái)創(chuàng)建一個(gè)代理,代理的就是對(duì)應(yīng)Handler的三種實(shí)現(xiàn)類(lèi)。在MyBatis工作時(shí),使用的StatementHandler 接口對(duì)象實(shí)際上就是 RoutingStatementHandler 對(duì)象。

BaseStatementHandler : 是 StatementHandler 接口的另一個(gè)實(shí)現(xiàn)類(lèi),它本身是一個(gè)抽象類(lèi),用于簡(jiǎn)化StatementHandler 接口實(shí)現(xiàn)的難度,屬于適配器設(shè)計(jì)模式體現(xiàn),它主要有三個(gè)實(shí)現(xiàn)類(lèi)

  • SimpleStatementHandler: 管理 Statement 對(duì)象并向數(shù)據(jù)庫(kù)中推送不需要預(yù)編譯的SQL語(yǔ)句。

  • PreparedStatementHandler: 管理 Statement 對(duì)象并向數(shù)據(jù)中推送需要預(yù)編譯的SQL語(yǔ)句。

  • CallableStatementHandler:管理 Statement 對(duì)象并調(diào)用數(shù)據(jù)庫(kù)中的存儲(chǔ)過(guò)程。

這里注意一下,SimpleStatementHandler 和 PreparedStatementHandler 的區(qū)別是 SQL 語(yǔ)句是否包含變量,是否通過(guò)外部進(jìn)行參數(shù)傳入。SimpleStatementHandler 用于執(zhí)行沒(méi)有任何參數(shù)傳入的 SQLPreparedStatementHandler 需要對(duì)外部傳入的變量和參數(shù)進(jìn)行提前參數(shù)綁定和賦值。

StatementHandler 的創(chuàng)建和源碼分析

我們繼續(xù)來(lái)分析上面 query 的調(diào)用鏈路,StatementHandler 的創(chuàng)建過(guò)程如下

MyBatis 會(huì)根據(jù) SQL 語(yǔ)句的類(lèi)型進(jìn)行對(duì)應(yīng) StatementHandler 的創(chuàng)建。我們以預(yù)處理 StatementHandler 為例來(lái)講解一下

執(zhí)行器不僅掌管著 StatementHandler 的創(chuàng)建,還掌管著創(chuàng)建 Statement 對(duì)象,設(shè)置參數(shù)等,在創(chuàng)建完 PreparedStatement 之后,我們需要對(duì)參數(shù)進(jìn)行處理了。

如果用一副圖來(lái)表示一下這個(gè)執(zhí)行流程的話(huà)我想是這樣

這里我們先暫停一下,來(lái)認(rèn)識(shí)一下第三個(gè)核心組件 ParameterHandler

ParameterHandler

ParameterHandler 介紹

ParameterHandler 相比于其他的組件就簡(jiǎn)單很多了,ParameterHandler 譯為參數(shù)處理器,負(fù)責(zé)為 PreparedStatement 的 sql 語(yǔ)句參數(shù)動(dòng)態(tài)賦值,這個(gè)接口很簡(jiǎn)單只有兩個(gè)方法

ParameterHandler 只有一個(gè)實(shí)現(xiàn)類(lèi) DefaultParameterHandler , 它實(shí)現(xiàn)了這兩個(gè)方法。

  • getParameterObject:用于讀取參數(shù)

  • setParameters: 用于對(duì) PreparedStatement 的參數(shù)賦值

ParameterHandler 的解析過(guò)程

上面我們討論過(guò)了 ParameterHandler 的創(chuàng)建過(guò)程,下面我們繼續(xù)上面 parameterSize 流程

這就是具體參數(shù)的解析過(guò)程了,下面我們來(lái)描述一下

public void setParameters(PreparedStatement ps) {

ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

// parameterMappings 就是對(duì) #{} 或者 ${} 里面參數(shù)的封裝

List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

if (parameterMappings != null) {

// 如果是參數(shù)化的SQL,便需要循環(huán)取出并設(shè)置參數(shù)的值

for (int i = 0; i < parameterMappings.size(); i++) {

ParameterMapping parameterMapping = parameterMappings.get(i);

// 如果參數(shù)類(lèi)型不是 OUT ,這個(gè)類(lèi)型與 CallableStatementHandler 有關(guān)

// 因?yàn)榇鎯?chǔ)過(guò)程不存在輸出參數(shù),所以參數(shù)不是輸出參數(shù)的時(shí)候,就需要設(shè)置。

if (parameterMapping.getMode() != ParameterMode.OUT) {

Object value;

// 得到 #{}  中的屬性名

String propertyName = parameterMapping.getProperty();

// 如果 propertyName 是 Map 中的key

if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params

// 通過(guò)key 來(lái)得到 additionalParameter 中的value值

value = boundSql.getAdditionalParameter(propertyName);

}

// 如果不是 additionalParameters 中的key,而且傳入?yún)?shù)是 null, 則value 就是null

else if (parameterObject == null) {

value = null;

}

// 如果 typeHandlerRegistry 中已經(jīng)注冊(cè)了這個(gè)參數(shù)的 Class 對(duì)象,即它是 Primitive 或者是String 的話(huà)

else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

value = parameterObject;

} else {

// 否則就是 Map

MetaObject metaObject = configuration.newMetaObject(parameterObject);

value = metaObject.getValue(propertyName);

}

// 在通過(guò) SqlSource 的parse 方法得到parameterMappings 的具體實(shí)現(xiàn)中,我們會(huì)得到parameterMappings 的 typeHandler

TypeHandler typeHandler = parameterMapping.getTypeHandler();

// 獲取 typeHandler 的jdbc type

JdbcType jdbcType = parameterMapping.getJdbcType();

if (value == null && jdbcType == null) {

jdbcType = configuration.getJdbcTypeForNull();

}

try {

typeHandler.setParameter(ps, i + 1, value, jdbcType);

} catch (TypeException e) {

throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

} catch (SQLException e) {

throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

}

}

}

}

}

下面用一個(gè)流程圖表示一下 ParameterHandler 的解析過(guò)程,以簡(jiǎn)單執(zhí)行器為例

我們?cè)谕瓿?ParameterHandler 對(duì) SQL 參數(shù)的預(yù)處理后,回到 SimpleExecutor 中的 doQuery 方法

上面又引出來(lái)了一個(gè)重要的組件那就是 ResultSetHandler,下面我們來(lái)認(rèn)識(shí)一下這個(gè)組件

ResultSetHandler

ResultSetHandler 簡(jiǎn)介

ResultSetHandler 也是一個(gè)非常簡(jiǎn)單的接口

ResultSetHandler 是一個(gè)接口,它只有一個(gè)默認(rèn)的實(shí)現(xiàn)類(lèi),像是 ParameterHandler 一樣,它的默認(rèn)實(shí)現(xiàn)類(lèi)是DefaultResultSetHandler

ResultSetHandler 解析過(guò)程

MyBatis 只有一個(gè)默認(rèn)的實(shí)現(xiàn)類(lèi)就是 DefaultResultSetHandler,DefaultResultSetHandler 主要負(fù)責(zé)處理兩件事

  • 處理 Statement 執(zhí)行后產(chǎn)生的結(jié)果集,生成結(jié)果列表

  • 處理存儲(chǔ)過(guò)程執(zhí)行后的輸出參數(shù)

按照 Mapper 文件中配置的 ResultType 或 ResultMap 來(lái)封裝成對(duì)應(yīng)的對(duì)象,最后將封裝的對(duì)象返回即可。

public List<Object> handleResultSets(Statement stmt) throws SQLException {

ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

final List<Object> multipleResults = new ArrayList<Object>();

int resultSetCount = 0;

// 獲取第一個(gè)結(jié)果集

ResultSetWrapper rsw = getFirstResultSet(stmt);

// 獲取結(jié)果映射

List<ResultMap> resultMaps = mappedStatement.getResultMaps();

// 結(jié)果映射的大小

int resultMapCount = resultMaps.size();

// 校驗(yàn)結(jié)果映射的數(shù)量

validateResultMapsCount(rsw, resultMapCount);

// 如果ResultSet 包裝器不是null, 并且 resultmap 的數(shù)量  >  resultSet 的數(shù)量的話(huà)

// 因?yàn)?resultSetCount 第一次肯定是0,所以直接判斷 ResultSetWrapper 是否為 0 即可

while (rsw != null && resultMapCount > resultSetCount) {

// 從 resultMap 中取出 resultSet 數(shù)量

ResultMap resultMap = resultMaps.get(resultSetCount);

// 處理結(jié)果集, 關(guān)閉結(jié)果集

handleResultSet(rsw, resultMap, multipleResults, null);

rsw = getNextResultSet(stmt);

cleanUpAfterHandlingResultSet();

resultSetCount++;

}

// 從 mappedStatement 取出結(jié)果集

String[] resultSets = mappedStatement.getResultSets();

if (resultSets != null) {

while (rsw != null && resultSetCount < resultSets.length) {

ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);

if (parentMapping != null) {

String nestedResultMapId = parentMapping.getNestedResultMapId();

ResultMap resultMap = configuration.getResultMap(nestedResultMapId);

handleResultSet(rsw, resultMap, null, parentMapping);

}

rsw = getNextResultSet(stmt);

cleanUpAfterHandlingResultSet();

resultSetCount++;

}

}

return collapseSingleResultList(multipleResults);

}

其中涉及的主要對(duì)象有:

ResultSetWrapper : 結(jié)果集的包裝器,主要針對(duì)結(jié)果集進(jìn)行的一層包裝,它的主要屬性有

  • ResultSet : Java JDBC ResultSet 接口表示數(shù)據(jù)庫(kù)查詢(xún)的結(jié)果。有關(guān)查詢(xún)的文本顯示了如何將查詢(xún)結(jié)果作為java.sql.ResultSet 返回。然后迭代此ResultSet以檢查結(jié)果。

  • TypeHandlerRegistry: 類(lèi)型注冊(cè)器,TypeHandlerRegistry 在初始化的時(shí)候會(huì)把所有的 Java類(lèi)型和類(lèi)型轉(zhuǎn)換器進(jìn)行注冊(cè)。

  • ColumnNames: 字段的名稱(chēng),也就是查詢(xún)操作需要返回的字段名稱(chēng)

  • ClassNames: 字段的類(lèi)型名稱(chēng),也就是 ColumnNames 每個(gè)字段名稱(chēng)的類(lèi)型

  • JdbcTypes: JDBC 的類(lèi)型,也就是 java.sql.Types 類(lèi)型

  • ResultMap: 負(fù)責(zé)處理更復(fù)雜的映射關(guān)系

在 DefaultResultSetHandler 中處理完結(jié)果映射,并把上述結(jié)構(gòu)返回給調(diào)用的客戶(hù)端,從而執(zhí)行完成一條完整的SQL語(yǔ)句。

文章參考:

MyBatis的優(yōu)缺點(diǎn)以及特點(diǎn)

mybatis基礎(chǔ),mybatis核心配置文件properties元素

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

深入淺出Mybatis系列(十)---SQL執(zhí)行流程分析(源碼篇)

https://www.jianshu.com/p/19686af69b0d

http://www.mybatis.org/mybatis-3/getting-started.html

https://www.cnblogs.com/virgosnail/p/10067964.html

https://blog.csdn.net/Roger_CoderLife/article/details/88707076

https://blog.csdn.net/qq924862077/article/details/52704191

[mybatis 源碼分析(八)ResultSetHandler 詳解]

[我要糾錯(cuò)]
文:王振袢&發(fā)表于江蘇
關(guān)鍵詞: 初識(shí) MyBatisMyBatis 第一個(gè) 支持 自定義

來(lái)源:本文內(nèi)容搜集或轉(zhuǎn)自各大網(wǎng)絡(luò)平臺(tái),并已注明來(lái)源、出處,如果轉(zhuǎn)載侵犯您的版權(quán)或非授權(quán)發(fā)布,請(qǐng)聯(lián)系小編,我們會(huì)及時(shí)審核處理。
聲明:江蘇教育黃頁(yè)對(duì)文中觀點(diǎn)保持中立,對(duì)所包含內(nèi)容的準(zhǔn)確性、可靠性或者完整性不提供任何明示或暗示的保證,不對(duì)文章觀點(diǎn)負(fù)責(zé),僅作分享之用,文章版權(quán)及插圖屬于原作者。

點(diǎn)個(gè)贊
0
踩一腳
0

您在閱讀:面試官問(wèn)你MyBatis SQL是如何執(zhí)行的?把這篇文章甩給他

Copyright?2013-2024 JSedu114 All Rights Reserved. 江蘇教育信息綜合發(fā)布查詢(xún)平臺(tái)保留所有權(quán)利

蘇公網(wǎng)安備32010402000125 蘇ICP備14051488號(hào)-3技術(shù)支持:南京博盛藍(lán)睿網(wǎng)絡(luò)科技有限公司

南京思必達(dá)教育科技有限公司版權(quán)所有   百度統(tǒng)計(jì)

主站蜘蛛池模板: 天天躁狠狠躁狠狠躁夜夜躁 | 成年人在线观看免费视频 | 露脸超嫩97后在线播放 | 中文字幕一区中文亚洲 | 日韩色视频一区二区三区亚洲 | 在线观看免费a∨网站 | 亚洲午夜高清 | 成人国产亚洲 | 国产三级a三级三级 | 国产成人在线视频播放 | 免费国产不卡午夜福在线观看 | 国产午夜精品视频 | 欧美性猛交ⅹxxx乱大交禽 | 国产a毛片高清视 | 色爽爽爽爽爽爽爽爽 | 天天干天天干天天干天天干天天干 | 欧美日韩中文一区二区三区 | 国产成人a∨麻豆精品 | 国产一区二区三区免费 | 日本免费一区二区三区看片 | 男女做污污无遮挡激烈免费 | 成人青草亚洲国产 | 亚洲成在人线久久综合 | 乡村乱肉第19部全文小说 | 免费在线视频一区 | 亚洲国产第一页 | 欧美成人精品一区二三区在线观看 | 狠狠成人 | 欧美日本在线一区二区三区 | 欧美视频在线免费播放 | 日本天天操 | 噜噜噜狠狠夜夜躁 | 天天做天天爽爽快快 | 欧美性xxx18一20 | 午夜怡红院 | 国产aa大片 | 国产dvd毛片在线视频 | 国产高清在线精品一区二区三区 | 在线播放国产一区 | 亚洲大成色www永久网址 | 窝窝社区在线观看www |
最熱文章
最新文章
  • 阿里云上云鉅惠,云產(chǎn)品享最低成本,有需要聯(lián)系,
  • 卡爾蔡司鏡片優(yōu)惠店,鏡片價(jià)格低
  • 蘋(píng)果原裝手機(jī)殼