**1. MyBatis 中 #{}和 ${}的區別是什么?**
#{}是預編譯處理,${}是字符替換。 在使用?#{}時,MyBatis 會將 SQL 中的?#{}替換成“?”,配合 PreparedStatement 的 set 方法賦值,這樣可以有效的防止 SQL 注入,保證程序的運行安全。
*****
**2. MyBatis 有幾種分頁方式?**
分頁方式:邏輯分頁和物理分頁。
邏輯分頁:?使用 MyBatis 自帶的 RowBounds 進行分頁,它是一次性查詢很多數據,然后在數據中再進行檢索。
物理分頁:?自己手寫 SQL 分頁或使用分頁插件 PageHelper,去數據庫查詢指定條數的分頁數據的形式。
*****
**3. RowBounds 是一次性查詢全部結果嗎?為什么?**
RowBounds 表面是在“所有”數據中檢索數據,其實并非是一次性查詢出所有數據,因為 MyBatis 是對 jdbc 的封裝,在 jdbc 驅動中有一個 Fetch Size 的配置,它規定了每次最多從數據庫查詢多少條數據,假如你要查詢更多數據,它會在你執行 next()的時候,去查詢更多的數據。就好比你去自動取款機取 10000 元,但取款機每次最多能取 2500 元,所以你要取 4 次才能把錢取完。只是對于 jdbc 來說,當你調用 next()的時候會自動幫你完成查詢工作。這樣做的好處可以有效的防止內存溢出。
*****
**4. MyBatis 邏輯分頁和物理分頁的區別是什么?**
- 邏輯分頁是一次性查詢很多數據,然后再在結果中檢索分頁的數據。這樣做弊端是需要消耗大量的內存、有內存溢出的風險、對數據庫壓力較大。
- 物理分頁是從數據庫查詢指定條數的數據,彌補了一次性全部查出的所有數據的種種缺點,比如需要大量的內存,對數據庫查詢壓力較大等問題。
*****
**5. MyBatis 是否支持延遲加載?延遲加載的原理是什么?**
MyBatis 支持延遲加載,設置 lazyLoadingEnabled=true 即可。
延遲加載的原理的是調用的時候觸發加載,而不是在初始化的時候就加載信息。比如調用 a. getB(). getName(),這個時候發現 a. getB() 的值為 null,此時會單獨觸發事先保存好的關聯 B 對象的 SQL,先查詢出來 B,然后再調用 a. setB(b),而這時候再調用 a. getB(). getName() 就有值了,這就是延遲加載的基本原理。
*****
**6. 說一下 MyBatis 的一級緩存和二級緩存?**
一級緩存:基于 PerpetualCache 的 HashMap 本地緩存,它的生命周期是和 SQLSession 一致的,有多個 SQLSession 或者分布式的環境中數據庫操作,可能會出現臟數據。當 Session flush 或 close 之后,該 Session 中的所有 Cache 就將清空,默認一級緩存是開啟的。
二級緩存:也是基于 PerpetualCache 的 HashMap 本地緩存,不同在于其存儲作用域為 Mapper 級別的,如果多個SQLSession之間需要共享緩存,則需要使用到二級緩存,并且二級緩存可自定義存儲源,如 Ehcache。默認不打開二級緩存,要開啟二級緩存,使用二級緩存屬性類需要實現 Serializable 序列化接口(可用來保存對象的狀態)。
開啟二級緩存數據查詢流程:二級緩存 -> 一級緩存 -> 數據庫。
緩存更新機制:當某一個作用域(一級緩存 Session/二級緩存 Mapper)進行了C/U/D 操作后,默認該作用域下所有 select 中的緩存將被 clear。
*****
**7. MyBatis 有哪些執行器(Executor)?**
- SimpleExecutor:每執行一次 update 或 select 就開啟一個 Statement 對象,用完立刻關閉 Statement 對象;
- ReuseExecutor:執行 update 或 select,以 SQL 作為 key 查找 Statement 對象,存在就使用,不存在就創建,用完后不關閉 Statement 對象,而是放置于 Map 內供下一次使用。簡言之,就是重復使用 Statement 對象;
- BatchExecutor:執行 update(沒有 select,jdbc 批處理不支持 select),將所有 SQL 都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個 Statement 對象,每個 Statement 對象都是 addBatch()完畢后,等待逐一執行 executeBatch()批處理,與 jdbc 批處理相同。
*****
**8. MyBatis 分頁插件的實現原理是什么?**
分頁插件的基本原理是使用 MyBatis 提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的 SQL,然后重寫 SQL,根據 dialect( 方言,mysql中指limit),添加對應的物理分頁語句和物理分頁參數。
*****
**9. MyBatis 如何編寫一個自定義插件?**
- Executor:攔截內部執行器,它負責調用 StatementHandler 操作數據庫,并把結果集通過 ResultSetHandler 進行自動映射,另外它還處理了二級緩存的操作;
- StatementHandler:攔截 SQL 語法構建的處理,它是 MyBatis 直接和數據庫執行 SQL 腳本的對象,另外它也實現了 MyBatis 的一級緩存;
- ParameterHandler:攔截參數的處理;
- ResultSetHandler:攔截結果集的處理。
自定義插件實現關鍵
MyBatis 插件要實現 Interceptor 接口,接口包含的方法,如下:
- setProperties 方法是在 MyBatis 進行配置插件的時候可以配置自定義相關屬性,即:接口實現對象的參數配置;
- plugin 方法是插件用于封裝目標對象的,通過該方法我們可以返回目標對象本身,也可以返回一個它的代理,可以決定是否要進行攔截進而決定要返回一個什么樣的目標對象,官方提供了示例:return Plugin. wrap(target, this);
- intercept 方法就是要進行攔截的時候要執行的方法。
*****
**10.通常一個 Xml 映射文件,都會寫一個 Dao 接口與之對應,
這個 Dao 接口的工作原理是什么?Dao 接口里的方法,
參數不同時,方法能重載嗎?**
Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;
接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值;接口方法內的
參數,就是傳遞給 sql 的參數。
Mapper 接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符
串作為 key 值,可唯一定位一個 MapperStatement。在 Mybatis 中,每一個
```
<select>、<insert>、<update>、<delete>
```
標簽,都會被解析為一個
MapperStatement 對象。
舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯
一找到 namespace 為 com.mybatis3.mappers.StudentDao 下面 id 為
findStudentById 的 MapperStatement。
Mapper 接口里的方法,是不能重載的,因為是使用 全限名+方法名 的保存和尋
找策略。Mapper 接口的工作原理是 JDK 動態代理,Mybatis 運行時會使用 JDK
動態代理為 Mapper 接口生成代理對象 proxy,代理對象會攔截接口方法,轉而
執行 MapperStatement 所代表的 sql,然后將 sql 執行結果返回。
*****
**11.Mybatis 動態 sql 有什么用?執行原理?有哪些動態 sql?**
Mybatis 動態 sql 可以在 Xml 映射文件內,以標簽的形式編寫動態 sql,執行原理
是根據表達式的值 完成邏輯判斷并動態拼接 sql 的功能。
Mybatis 提供了 9 種動態 sql 標簽:trim | where | set | foreach | if | choose
| when | otherwise | bind。
*****
**12.什么是 MyBatis 的接口綁定?有哪些實現方式?**
接口綁定,就是在 MyBatis 中任意定義接口,然后把接口里面的方法和 SQL 語句綁
定, 我們直接調用接口方法就可以,這樣比起原來了 SqlSession 提供的方法我們可
以有更加靈活的選擇和設置。
接口綁定有兩種實現方式,一種是通過注解綁定,就是在接口的方法上面加上
@Select、@Update 等注解,里面包含 Sql 語句來綁定;另外一種就是通過 xml
里面寫 SQL 來綁定, 在這種情況下,要指定 xml 映射文件里面的 namespace 必須
為接口的全路徑名。當 Sql 語句比較簡單時候,用注解綁定, 當 SQL 語句比較復雜
時候,用 xml 綁定,一般用 xml 綁定的比較多。
*****
**13.使用 MyBatis 的 mapper 接口調用時有哪些要求?**
- Mapper 接口方法名和 mapper.xml 中定義的每個 sql 的 id 相同;
- Mapper 接口方法的輸入參數類型和 mapper.xml 中定義的每個 sql 的
parameterType 的類型相同;
- Mapper 接口方法的輸出參數類型和 mapper.xml 中定義的每個 sql 的
resultType 的類型相同;
- Mapper.xml 文件中的 namespace 即是 mapper 接口的類路徑。