你當前所在位置:首頁 > IT就業問題 > Java面試題及答案(13/19)——Mybatis

Java面試題及答案(13/19)——Mybatis

125. MyBatis 中 #{}和 ${}的區別是什么?

 

#{}是預編譯處理,${}是字符替換。 在使用 #{}時,MyBatis 會將 SQL 中的#{}替換成“?”,配合 PreparedStatement 的 set 方法賦值,這樣可以有效的防止 SQL 注入,保證程序的運行安全。

 

126. MyBatis 有幾種分頁方式?

分頁方式:邏輯分頁和物理分頁。

 

邏輯分頁: 使用 MyBatis 自帶的 RowBounds 進行分頁,它是一次性查詢很多數據,然后在數據中再進行檢索。

 

物理分頁: 自己手寫 SQL 分頁或使用分頁插件 PageHelper,去數據庫查詢指定條數的分頁數據的形式。

 

127. RowBounds 是一次性查詢全部結果嗎?為什么? 

RowBounds 表面是在“所有”數據中檢索數據,其實并非是一次性查詢出所有數據,因為 MyBatis 是對 jdbc 的封裝,在 jdbc 驅動中有一個 Fetch Size 的配置,它規定了每次最多從數據庫查詢多少條數據,假如你要查詢更多數據,它會在你執行 next()的時候,去查詢更多的數據。

就好比你去自動取款機取 10000 元,但取款機每次最多能取 2500 元,所以你要取 4 次才能把錢取完。只是對于 jdbc 來說,當你調用 next()的時候會自動幫你完成查詢工作。這樣做的好處可以有效的防止內存溢出。

 

128. MyBatis 邏輯分頁和物理分頁的區別是什么?

邏輯分頁是一次性查詢很多數據,然后再在結果中檢索分頁的數據。這樣做弊端是需要消耗大量的內存、有內存溢出的風險、對數據庫壓力較大。

 

物理分頁是從數據庫查詢指定條數的數據,彌補了一次性全部查出的所有數據的種種缺點,比如需要大量的內存,對數據庫查詢壓力較大等問題。

 

129. MyBatis 是否支持延遲加載?延遲加載的原理是什么?

MyBatis 支持延遲加載,設置 lazyLoadingEnabled=true 即可。

延遲加載的原理是調用的時候觸發加載,而不是在初始化的時候就加載信息。比如調用 a. getB(). getName(),這個時候發現 a. getB() 的值為 null,此時會單獨觸發事先保存好的關聯 B 對象的 SQL,先查詢出來 B,然后再調用 a. setB(b),而這時候再調用 a. getB(). getName() 就有值了,這就是延遲加載的基本原理。

130. 說一下 MyBatis 的一級緩存和二級緩存?

一級緩存:基于 PerpetualCache 的 HashMap 本地緩存,它的聲明周期是和 SQLSession 一致的,有多個 SQLSession 或者分布式的環境中數據庫操作,可能會出現臟數據。當 Session flush 或 close 之后,該 Session 中的所有 Cache 就將清空,默認一級緩存是開啟的。

二級緩存:也是基于 PerpetualCache 的 HashMap 本地緩存,不同在于其存儲作用域為 Mapper 級別的,如果多個SQLSession之間需要共享緩存,則需要使用到二級緩存,并且二級緩存可自定義存儲源,如 Ehcache。默認不打開二級緩存,要開啟二級緩存,使用二級緩存屬性類需要實現 Serializable 序列化接口(可用來保存對象的狀態)。

開啟二級緩存數據查詢流程:二級緩存 -> 一級緩存 -> 數據庫。

緩存更新機制:當某一個作用域(一級緩存 Session/二級緩存 Mapper)進行了C/U/D 操作后,默認該作用域下所有 select 中的緩存將被 clear。

131. MyBatis 和 hibernate 的區別有哪些?

靈活性:MyBatis 更加靈活,自己可以寫 SQL 語句,使用起來比較方便。


可移植性:MyBatis 有很多自己寫的 SQL,因為每個數據庫的 SQL 可以不相同,所以可移植性比較差。


學習和使用門檻:MyBatis 入門比較簡單,使用門檻也更低。


二級緩存:hibernate 擁有更好的二級緩存,它的二級緩存可以自行更換為第三方的二級緩存。

132. MyBatis 有哪些執行器(Executor)?

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 批處理相同。

133. MyBatis 分頁插件的實現原理是什么?

分頁插件的基本原理是使用 MyBatis 提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的 SQL,然后重寫 SQL,根據 dialect 方言,添加對應的物理分頁語句和物理分頁參數。

134. MyBatis 如何編寫一個自定義插件?

自定義插件實現原理

MyBatis 自定義插件針對 MyBatis 四大對象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)進行攔截:

Executor:攔截內部執行器,它負責調用 StatementHandler 操作數據庫,并把結果集通過 ResultSetHandler 進行自動映射,另外它還處理了二級緩存的操作;


StatementHandler:攔截 SQL 語法構建的處理,它是 MyBatis 直接和數據庫執行 SQL 腳本的對象,另外它也實現了 MyBatis 的一級緩存;


ParameterHandler:攔截參數的處理;


ResultSetHandler:攔截結果集的處理。

自定義插件實現關鍵

MyBatis 插件要實現 Interceptor 接口,接口包含的方法,如下:

public interface Interceptor {   

   Object intercept(Invocation invocation) throws Throwable;       

   Object plugin(Object target);    

   void setProperties(Properties properties);

}


setProperties 方法是在 MyBatis 進行配置插件的時候可以配置自定義相關屬性,即:接口實現對象的參數配置;

plugin 方法是插件用于封裝目標對象的,通過該方法我們可以返回目標對象本身,也可以返回一個它的代理,可以決定是否要進行攔截進而決定要返回一個什么樣的目標對象,官方提供了示例:return Plugin. wrap(target, this);

intercept 方法就是要進行攔截的時候要執行的方法。

自定義插件實現示例

官方插件實現:

 

@Intercepts({@Signature(type = Executor. class, method = "query",

        args = {MappedStatement. class, Object. class, RowBounds. class, ResultHandler. class})})public class TestInterceptor implements Interceptor {

   public Object intercept(Invocation invocation) throws Throwable {

     Object target = invocation. getTarget(); //被代理對象

     Method method = invocation. getMethod(); //代理方法

     Object[] args = invocation. getArgs(); //方法參數

     // do something . . . . . .  方法攔截前執行代碼塊

     Object result = invocation. proceed();

     // do something . . . . . . . 方法攔截后執行代碼塊

     return result;

   }

   public Object plugin(Object target) {

     return Plugin. wrap(target, this);

   }

}


課程預約

极速1分彩_Welcome