10道不得不會(huì)的MyBatis面試題

MyBatis

本系列《最少必要面試題》

1. 什么是MyBatis

這個(gè)問題主要是對(duì)比JDBC來看

  1. MyBatis是一個(gè)ORM(對(duì)象關(guān)系映射)框架,它內(nèi)部封裝了JDBC,開發(fā)時(shí)只需要關(guān)注SQL語句本身,不需要花費(fèi)精力去處理加載驅(qū)動(dòng),創(chuàng)建連接,創(chuàng)建statement等復(fù)雜的過程。開發(fā)人員不需要編寫原生態(tài)sql,可以嚴(yán)格控制sql執(zhí)行性能,靈活度高。

  2. MyBatis可以使用xml或者注解來配置映射原生信息,將POJO映射成數(shù)據(jù)庫中的記錄,避免了幾乎所有的JDBC代碼和手動(dòng)設(shè)置的參數(shù)以及獲取結(jié)果集。

2. MyBatis的優(yōu)點(diǎn)

  1. 基于SQL語句編程,相對(duì)靈活(相對(duì)于hibernate),支持寫動(dòng)態(tài)sql語句并可重復(fù)使用。

  2. 減少代碼量,消除了冗余代碼。(類似于JDBC的封裝)

  3. 與Spring完美集成。

  4. 提供映射標(biāo)簽支持字段關(guān)系映射。

3. #{}和${}的區(qū)別是什么?

  1. {}預(yù)編譯處理、是占位符,${}是字符串替換、是拼接符。

  2. 使用#{}可以有效的防止sql注入,提高系統(tǒng)的安全性。

Mybatis在處理#{}的時(shí)候會(huì)將sql中的#{}替換成?號(hào),調(diào)用PreparedStatement來賦值

/* SQL */
如:select * from user where name = #{userName};設(shè)userName=javapub

看日志我們可以看到解析時(shí)將#{userName}替換成了 ?

select * from user where name = ?;

然后再把 javapub 放進(jìn)去,外面加上單引號(hào)

Mybatis在處理{}的時(shí)候就是把{}替換成變量的值,調(diào)用Statement來賦值

/* SQL */
如:select * from user where name = #{userName};設(shè)userName=javapub

看日志可以發(fā)現(xiàn)就是直接把值拼接上去了

select * from user where name = javapub;

這極有可能發(fā)生sql注入,下面舉了一個(gè)簡單的sql注入案例

4. 一個(gè) Xml 映射文件,都會(huì)寫一個(gè) Dao 接口與之對(duì)應(yīng),這個(gè) Dao 接口的工作原理是什么?

Dao 接口就是人們常說的 Mapper 接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名就是映射文件中 MappedStatement 的 id 值,接口方法內(nèi)的參數(shù)就是傳遞給 sql 的參數(shù)。

接口里的方法是不能重載的,因?yàn)槭?strong>全限名+方法名的保存和尋找策略。

Dao接口的工作原理是JDK動(dòng)態(tài)代理,Mybatis運(yùn)行時(shí)會(huì)使用JDK動(dòng)態(tài)代理為Dao接口生成代理proxy對(duì)象,代理對(duì)象proxy會(huì)攔截接口方法,轉(zhuǎn)而執(zhí)行接口方法所對(duì)應(yīng)的MappedStatement所代表的sql,然后將sql執(zhí)行結(jié)果返回。

MappedStatement:MappedStatement維護(hù)了一條 <select|update|delete|insert>節(jié)點(diǎn)的封裝,包括了傳入?yún)?shù)映射配置、執(zhí)行的SQL語句、結(jié)果映射配置等信息。

<select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap">
        select id, username from author where id = #{value}
</select>

5. 如何獲取自動(dòng)生成的(主)鍵值?

用法:

<insert /> 標(biāo)簽中添加 useGeneratedKeys="true" 等屬性

<insert id="insert" useGeneratedKeys="true" keyProperty="id" keyColumn="id"
        parameterType="person" >
    INSERT INTO person(name, pswd)
    VALUE (#{name}, #{pswd})
</insert>

當(dāng) Mybatis 解析 xml節(jié)點(diǎn)是,讀到 insert 有配置時(shí),會(huì)判斷是否 有配置 useGeneratedKeys,如果有則會(huì)使用 Jdbc3KeyGenerator 作為sql回顯,否則會(huì)以 NoKeyGenerator 作為主鍵回顯。

底層封裝了JDBC獲取自增主鍵,即當(dāng)使用 prepareStatement 或者 Statement時(shí)候,可以通過 getGeneratedKeys 獲取 當(dāng)條插入語句的自增而成的主鍵。例子

    Connection conn = DriverManager.getConnection(url, "root", "123456");
    String[] columnNames = {"id", "name"};
    PreparedStatement stmt = conn.prepareStatement(sql, columnNames);
    stmt.setString(1, "jack wang");
    stmt.executeUpdate();
    ResultSet rs = stmt.getGeneratedKeys();
    int id = 0;
    if (rs.next()) {
        id = rs.getInt(1);
        System.out.println("----------" + id);
    }

6. Mybatis 動(dòng)態(tài) sql 有什么用?有哪些動(dòng)態(tài) sql?執(zhí)行原理?

Mybatis 動(dòng)態(tài) sql 可以讓我們?cè)?Xml 映射文件內(nèi),以標(biāo)簽的形式編寫動(dòng)態(tài) sql,完成邏輯判斷和動(dòng)態(tài)拼接 sql 的功能。

Mybatis 提供了9種動(dòng)態(tài)sql標(biāo)簽: trim | where | set | foreach | if | choose | when | otherwise | bind。

其執(zhí)行原理為,使用 OGNL 從 sql 參數(shù)對(duì)象中計(jì)算表達(dá)式的值,根據(jù)表達(dá)式的值動(dòng)態(tài)拼接 sql,以此來完成動(dòng)態(tài) sql 的功能。

是不是有點(diǎn)懵,繼續(xù)閱讀:

科普:

OGNL 是 Object-Graph Navigation Language 的縮寫,對(duì)象-圖行導(dǎo)航語言。例如 #{} 語法。

OGNL 作用是在對(duì)象和視圖之間做數(shù)據(jù)的交互,可以存取對(duì)象的屬性和調(diào)用對(duì)象的方法,通過表達(dá)式可以迭代出整個(gè)對(duì)象的結(jié)構(gòu)圖。

參考一個(gè)很形象的例子。

有一個(gè)學(xué)生對(duì)象 student,屬性分別有 id = 10,name = '小明' 和 課程對(duì)象 course,其中 course 對(duì)象中屬性有:分?jǐn)?shù) score = 88,排名 rank = 5。

對(duì)象關(guān)系圖如下:

student

    id:10

    name:小明

    course:

        score:88

        rank:5

當(dāng)上下文(環(huán)境)中的對(duì)象為 student 的時(shí)候,也就是在 Mybatis 中查詢時(shí)傳入的參數(shù)對(duì)象為 student 的時(shí)候:

通過 OGNL 表達(dá)式直接獲取上下文中對(duì)象的屬性值,比如:

{id} —> 10,相對(duì)于當(dāng)前上下文對(duì)象.getId(),即 student.getId() 。

{name} —> 小明。

{course.score} —> 88,相當(dāng)于 student.getCourse().getScore()。

所以,通過 OGNL 表達(dá)式,可以迭代出整個(gè)對(duì)象的結(jié)構(gòu)圖。

發(fā)布 《最少必要面試題》

7. 什么是Mybatis的一級(jí)、二級(jí)緩存?

一級(jí)緩存: 基于 PerpetualCache 的 HashMap 本地緩存,其存儲(chǔ)作用域?yàn)?Session,當(dāng) Session flush 或 close 之后,該 Session 中的所有 Cache 就將清空,默認(rèn)一級(jí)緩存是開啟的。

當(dāng)Mybaits與Spring整合的時(shí)候,不帶Spring事務(wù)的方法內(nèi),每次請(qǐng)求數(shù)據(jù)庫,都會(huì)新建一個(gè)SqlSession,這時(shí)候是使用不到一級(jí)緩存的。除了事務(wù)問題,還有調(diào)用了Sqlsession的修改、添加、刪除、commit()、close()等方法時(shí),一級(jí)緩存也會(huì)被清空。

二級(jí)緩存與一級(jí)緩存其機(jī)制相同,默認(rèn)也是采用 PerpetualCache,HashMap 存儲(chǔ),不同在于其存儲(chǔ)作用域?yàn)?Mapper(Namespace)。即使開啟了二級(jí)緩存,不同的sqlsession之間的緩存數(shù)據(jù)也不是想互訪就能互訪的,必須等到sqlsession關(guān)閉了以后,才會(huì)把其一級(jí)緩存中的數(shù)據(jù)寫入二級(jí)緩存。默認(rèn)不打開二級(jí)緩存。

現(xiàn)在大多數(shù)應(yīng)用都是支持分布式的,一般情況都是用中間件作為緩存層,比如redis。開啟 MyBatis 的二級(jí)緩存也會(huì)多一步序列化和反序列化,影響服務(wù)性能。

8. MyBatis的工作原理

一圖勝千文

[圖片上傳失敗...(image-2c3ddd-1655967208925)]

  1. 讀取 MyBatis 配置文件:mybatis-config.xml 為 MyBatis 的全局配置文件,配置了 MyBatis 的運(yùn)行環(huán)境等信息,例如數(shù)據(jù)庫連接信息。

  2. 加載映射文件。映射文件即 SQL 映射文件,該文件中配置了操作數(shù)據(jù)庫的 SQL 語句,需要在 MyBatis 配置文件 mybatis-config.xml 中加載。mybatis-config.xml 文件可以加載多個(gè)映射文件,每個(gè)文件對(duì)應(yīng)數(shù)據(jù)庫中的一張表。

  3. 構(gòu)造會(huì)話工廠:通過 MyBatis 的環(huán)境等配置信息構(gòu)建會(huì)話工廠 SqlSessionFactory。

  4. 創(chuàng)建會(huì)話對(duì)象:由會(huì)話工廠創(chuàng)建 SqlSession 對(duì)象,該對(duì)象中包含了執(zhí)行 SQL 語句的所有方法。

  5. Executor 執(zhí)行器:MyBatis 底層定義了一個(gè) Executor 接口來操作數(shù)據(jù)庫,它將根據(jù) SqlSession 傳遞的參數(shù)動(dòng)態(tài)地生成需要執(zhí)行的 SQL 語句,同時(shí)負(fù)責(zé)查詢緩存的維護(hù)。

  6. MappedStatement 對(duì)象:在 Executor 接口的執(zhí)行方法中有一個(gè) MappedStatement 類型的參數(shù),該參數(shù)是對(duì)映射信息的封裝,用于存儲(chǔ)要映射的 SQL 語句的 id、參數(shù)等信息。

  7. 輸入?yún)?shù)映射:輸入?yún)?shù)類型可以是 Map、List 等集合類型,也可以是基本數(shù)據(jù)類型和 POJO 類型。輸入?yún)?shù)映射過程類似于 JDBC 對(duì) preparedStatement 對(duì)象設(shè)置參數(shù)的過程。

  8. 輸出結(jié)果映射:輸出結(jié)果類型可以是 Map、 List 等集合類型,也可以是基本數(shù)據(jù)類型和 POJO 類型。輸出結(jié)果映射過程類似于 JDBC 對(duì)結(jié)果集的解析過程。

9. 什么是MyBatis的接口綁定?有哪些實(shí)現(xiàn)方式?

接口綁定,就是在 MyBatis 中任意定義接口,然后把接口里面的方法和SQL語句綁定,我們直接調(diào)用接口方法就可以,這樣比起原來了SqlSession提供的方法我們可以有更加靈活的選擇和設(shè)置。

接口綁定有兩種實(shí)現(xiàn)方式:

  • 通過注解綁定,就是在接口的方法上面加上 @Select、@Update 等注解,里面包含Sql語句來綁定;

  • 通過xml里面寫SQL來綁定, 在這種情況下,要指定xml映射文件里面的 namespace 必須為接口的全路徑名。當(dāng)Sql語句比較簡單時(shí)候,用注解綁定, 當(dāng)SQL語句比較復(fù)雜時(shí)候,用xml綁定,一般用xml綁定的比較多。

10. Mybatis的分頁原理

Mybatis 使用 RowBounds 對(duì)象進(jìn)行分頁,它是針對(duì)ResultSet結(jié)果集執(zhí)行的內(nèi)存分頁,而非物理分頁,所以一般不會(huì)使用??梢栽趕ql內(nèi)直接書寫帶有物理分頁的參數(shù)來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。

分頁插件的原理就是使用 MyBatis 提供的插件接口,實(shí)現(xiàn)自定義插件,在插件的攔截方法內(nèi),攔截待執(zhí)行的SQL,然后根據(jù)設(shè)置的 dialect(方言),和設(shè)置的分頁參數(shù),重寫SQL ,生成帶有分頁語句的SQL,執(zhí)行重寫后的SQL,從而實(shí)現(xiàn)分頁。

舉例:select * from student,攔截sql后重寫為:select t.* from (select * from student)t limit 0,10。

[圖片上傳失敗...(image-998e03-1655967208925)]

低谷蓄力

《最少必要面試題》

10道不得不會(huì)的Java基礎(chǔ)面試題

10道不得不會(huì)的MySQL基礎(chǔ)面試題

10道不得不會(huì)的Java并發(fā)基礎(chǔ)面試題

10道不得不會(huì)的JVM面試題

10道不得不會(huì)的ElasticSearch面試題

10道不得不會(huì)的Spring面試題

10道不得不會(huì)的 Redis 面試題

10道不得不會(huì)的 Kafka 面試題

10道不得不會(huì)的 MyBatis 面試題

10道不得不會(huì)的 Docker 面試題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容