三天吃透mybatis面試八股文

程式設計師大彬發表於2023-03-13

本文已經收錄到Github倉庫,該倉庫包含計算機基礎、Java基礎、多執行緒、JVM、資料庫、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分散式、微服務、設計模式、架構、校招社招分享等核心知識點,歡迎star~

Github地址:https://github.com/Tyson0314/Java-learning


Mybatis是什麼?

  • MyBatis框架是一個開源的資料持久層框架。
  • 它的內部封裝了透過JDBC訪問資料庫的操作,支援普通的SQL查詢、儲存過程和高階對映,幾乎消除了所有的JDBC程式碼和引數的手工設定以及結果集的檢索。
  • MyBatis作為持久層框架,其主要思想是將程式中的大量SQL語句剝離出來,配置在配置檔案當中,實現SQL的靈活配置。
  • 這樣做的好處是將SQL與程式程式碼分離,可以在不修改程式碼的情況下,直接在配置檔案當中修改SQL。

ORM是什麼

ORM(Object Relational Mapping),物件關係對映,是一種為了解決關係型資料庫資料與簡單Java物件(POJO)的對映關係的技術。簡單的說,ORM是透過使用描述物件和資料庫之間對映的後設資料,將程式中的物件自動持久化到關係型資料庫中。

Mybatis和Hibernate的區別?

主要有以下幾點區別:

  1. Hibernate的開發難度大於MyBatis,主要由於Hibernate比較複雜,龐大,學習週期比較長。
  2. Hibernate屬於全自動ORM對映工具,使用Hibernate查詢關聯物件或者關聯集合物件時,可以根據物件關係模型直接獲取,所以它是全自動的。而Mybatis在查詢關聯物件或關聯集合物件時,需要手動編寫sql來完成,所以,稱之為半自動ORM對映工具。
  3. 資料庫擴充套件性的區別。Hibernate與資料庫具體的關聯在XML中,所以HQL對具體是用什麼資料庫並不是很關心。MyBatis由於所有sql都是依賴資料庫書寫的,所以擴充套件性、遷移性比較差。
  4. 快取機制的區別。Hibernate的二級快取配置在SessionFactory生成配置檔案中進行詳細配置,然後再在具體的表物件對映中配置那種快取。MyBatis的二級快取配置都是在每個具體的表物件對映中進行詳細配置,這樣針對不同的表可以自定義不同的緩衝機制,並且MyBatis可以在名稱空間中共享相同的快取配置和例項,透過Cache-ref來實現。
  5. 日誌系統完善性的區別。Hibernate日誌系統非常健全,涉及廣泛,而Mybatis則除了基本記錄功能外,功能薄弱很多。
  6. sql的最佳化上,Mybatis要比Hibernate方便很多。由於Mybatis的sql都是寫在xml裡,因此最佳化sql比Hibernate方便很多。而Hibernate的sql很多都是自動生成的,無法直接維護sql;總之寫sql的靈活度上Hibernate不及Mybatis。

MyBatis框架的優缺點及其適用的場合

優點

  1. 與JDBC相比,減少了50%以上的程式碼量。
  2. MyBatis是易學的持久層框架,小巧並且簡單易學。
  3. MyBatis相當靈活,不會對應用程式或者資料庫的現有設計強加任何影響,SQL寫在XML檔案裡,從程式程式碼中徹底分離,降低耦合度,便於統一的管理和最佳化,並可重用。
  4. 提供XML標籤,支援編寫動態的SQL,滿足不同的業務需求。
  5. 提供對映標籤,支援物件與資料庫的ORM欄位關係對映。

缺點

  1. SQL語句的編寫工作量較大,對開發人員編寫SQL的能力有一定的要求。
  2. SQL語句依賴於資料庫,導致資料庫不具有好的移植性,不可以隨便更換資料庫。

適用場景

MyBatis專注於SQL自身,是一個足夠靈活的DAO層解決方案。對效能的要求很高,或者需求變化較多的專案,例如Web專案,那麼MyBatis是不二的選擇。

Mybatis的工作原理

  • 讀取MyBatis配置檔案:mybatis-config.xml為MyBatis的全域性配置檔案,配置了MyBatis的執行環境等資訊,例如資料庫連線資訊。
  • 載入對映檔案。對映檔案即SQL對映檔案,該檔案中配置了運算元據庫的SQL語句,需要在MyBatis配置檔案mybatis-config.xml中載入。mybatis-config.xml檔案可以載入多個對映檔案,每個檔案對應資料庫中的一張表。
  • 構造會話工廠:透過MyBatis的環境等配置資訊構建會話工廠SqlSessionFactory。
  • 建立會話物件:由會話工廠建立SqlSession物件,該物件中包含了執行SQL語句的所有方法。
  • Executor執行器:MyBatis底層定義了一個Executor 介面來運算元據庫,它將根據SqlSession傳遞的引數動態地生成需要執行的SQL語句,同時負責查詢快取的維護。
  • MappedStatement 物件:在Executor介面的執行方法中有一個MappedStatement型別的引數,該引數是對對映資訊的封裝,用於儲存要對映的SQL語句的id、引數等資訊。
  • 輸入引數對映:輸入引數型別可以是Map、List等集合型別,也可以是基本資料型別和POJO型別。輸入引數對映過程類似於 JDBC對preparedStatement物件設定引數的過程。
  • 輸出結果對映:輸出結果型別可以是Map、List等集合型別,也可以是基本資料型別和POJO型別。輸出結果對映過程類似於 JDBC對結果集的解析過程。

Mybatis都有哪些Executor執行器?它們之間的區別是什麼?

Mybatis有三種基本的Executor執行器,SimpleExecutorReuseExecutorBatchExecutor

SimpleExecutor:每執行一次update或select,就開啟一個Statement物件,用完立刻關閉Statement物件。

ReuseExecutor:執行update或select,以sql作為key查詢Statement物件,存在就使用,不存在就建立,用完後,不關閉Statement物件,而是放置於Map<String, Statement>內,供下一次使用。簡言之,就是重複使用Statement物件。

BatchExecutor:執行update(沒有select,JDBC批處理不支援select),將所有sql都新增到批處理中(addBatch()),等待統一執行(executeBatch()),它快取了多個Statement物件,每個Statement物件都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。

作用範圍:Executor的這些特點,都嚴格限制在SqlSession生命週期範圍內。

MyBatis中介面繫結有幾種實現方式?

  1. 透過註解繫結,在介面的方法上面加上 @Select@Update等註解裡面包含Sql語句來繫結(SQL語句比較簡單的時候,推薦註解繫結)
  2. 透過xml裡面寫SQL來繫結, 指定xml對映檔案裡面的namespace必須為介面的全路徑名(SQL語句比較複雜的時候,推薦xml繫結)

Mybatis 是如何進行分頁的?

Mybatis 使用 RowBounds 物件進行分頁,它是針對 ResultSet 結果集執行的記憶體分頁,而非物理分頁,先把資料都查出來,然後再做分頁。

可以在 sql 內直接書寫帶有物理分頁的引數來完成物理分頁功能,也可以使用分頁外掛來完成物理分頁。

分頁外掛的基本原理是什麼?

分頁外掛的基本原理是使用 Mybatis 提供的外掛介面,實現自定義外掛,在外掛的攔截方法內攔截待執行的 sql,然後重寫 sql(SQL 拼接 limit),根據 dialect 方言,新增對應的物理分頁語句和物理分頁引數,用到了技術 JDK 動態代理,用到了責任鏈設計模式。

簡述Mybatis的外掛執行原理

Mybatis僅可以編寫針對 ParameterHandlerResultSetHandlerStatementHandlerExecutor這4種介面的外掛,Mybatis使用JDK的動態代理,為需要攔截的介面生成代理物件以實現介面方法攔截功能,每當執行這4種介面物件的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,當然,只會攔截那些你指定需要攔截的方法。

.如何編寫一個外掛?

編寫外掛:實現 Mybatis 的 Interceptor 介面並複寫 intercept()方法,然後再給外掛編寫註解,指定要攔截哪一個介面的哪些方法即可,最後在配置檔案中配置你編寫的外掛。

.Mybatis 是否支援延遲載入?

Mybatis 僅支援 association 關聯物件和 collection 關聯集合物件的延遲載入,association 指的就是一對一,collection 指的就是一對多查詢。在 Mybatis 配置檔案中,可以配置是否啟用延遲載入lazyLoadingEnabled=true|false

延遲載入的基本原理是什麼?

延遲載入的基本原理是,使用 CGLIB 建立目標物件的代理物件,當呼叫目標方法時,進入攔截器方法。

比如呼叫a.getB().getName(),攔截器 invoke()方法發現 a.getB()是 null 值,那麼就會單獨傳送事先儲存好的查詢關聯 B 物件的 sql,把 B 查詢上來,然後呼叫a.setB(b),於是 a 的物件 b 屬性就有值了,接著完成a.getB().getName()方法的呼叫。

當然了,不光是 Mybatis,幾乎所有的包括 Hibernate,支援延遲載入的原理都是一樣的。

{}和${}的區別是什麼?

{ } 被解析成預編譯語句,預編譯之後可以直接執行,不需要重新編譯sql。

//sqlMap 中如下的 sql 語句
select * from user where name = #{name};
//解析成為預編譯語句;編譯好SQL語句再取值
select * from user where name = ?;

${ } 僅僅為一個字串替換,每次執行sql之前需要進行編譯,存在 sql 注入問題。

select * from user where name = '${name}'
//傳遞的引數為 "ruhua" 時,解析為如下,然後傳送資料庫伺服器進行編譯。取值以後再去編譯SQL語句。
select * from user where name = "ruhua";

Mybatis的預編譯

資料庫接受到sql語句之後,需要詞法和語義解析,最佳化sql語句,制定執行計劃。這需要花費一些時間。如果一條sql語句需要反覆執行,每次都進行語法檢查和最佳化,會浪費很多時間。預編譯語句就是將sql語句中的值用佔位符替代,即將sql語句模板化。一次編譯、多次執行,省去了解析最佳化等過程。

mybatis是透過PreparedStatement和佔位符來實現預編譯的。

mybatis底層使用PreparedStatement,預設情況下,將對所有的 sql 進行預編譯,將#{}替換為?,然後將帶有佔位符?的sql模板傳送至mysql伺服器,由伺服器對此無引數的sql進行編譯後,將編譯結果快取,然後直接執行帶有真實引數的sql。

預編譯的作用:

  1. 預編譯階段可以最佳化 sql 的執行。預編譯之後的 sql 多數情況下可以直接執行,資料庫伺服器不需要再次編譯,可以提升效能。
  2. 預編譯語句物件可以重複利用。把一個 sql 預編譯後產生的 PreparedStatement 物件快取下來,下次對於同一個sql,可以直接使用這個快取的 PreparedState 物件。
  3. 防止SQL隱碼攻擊。使用預編譯,而其後注入的引數將不會再進行SQL編譯。也就是說其後注入進來的引數系統將不會認為它會是一條SQL語句,而預設其是一個引數。

    ## 一級快取和二級快取

快取:合理使用快取是最佳化中最常見的方法之一,將從資料庫中查詢出來的資料放入快取中,下次使用時不必從資料庫查詢,而是直接從快取中讀取,避免頻繁運算元據庫,減輕資料庫的壓力,同時提高系統效能。

一級快取是SqlSession級別的快取:Mybatis對快取提供支援,預設情況下只開啟一級快取,一級快取作用範圍為同一個SqlSession。在SQL和引數相同的情況下,我們使用同一個SqlSession物件呼叫同一個Mapper方法,往往只會執行一次SQL。因為在使用SqlSession第一次查詢後,Mybatis會將結果放到快取中,以後再次查詢時,如果沒有宣告需要重新整理,並且快取沒超時的情況下,SqlSession只會取出當前快取的資料,不會再次傳送SQL到資料庫。若使用不同的SqlSession,因為不同的SqlSession是相互隔離的,不會使用一級快取。

二級快取是mapper級別的快取:可以使快取在各個SqlSession之間共享。二級快取預設不開啟,需要在mybatis-config.xml開啟二級快取:

<!-- 通知 MyBatis 框架開啟二級快取 -->
<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>

並在相應的Mapper.xml檔案新增cache標籤,表示對哪個mapper 開啟快取:

<cache/>

二級快取要求返回的POJO必須是可序列化的,即要求實現Serializable介面。

當開啟二級快取後,資料的查詢執行的流程就是 二級快取 -> 一級快取 -> 資料庫。


最後給大家分享一個Github倉庫,上面有大彬整理的300多本經典的計算機書籍PDF,包括C語言、C++、Java、Python、前端、資料庫、作業系統、計算機網路、資料結構和演算法、機器學習、程式設計人生等,可以star一下,下次找書直接在上面搜尋,倉庫持續更新中~

Github地址https://github.com/Tyson0314/java-books

相關文章