113. 為什么要使用 hibernate?
- hibernate 是對 jdbc 的封裝,大大簡化了數據訪問層的繁瑣的重復性代碼。
- hibernate 是一個優秀的 ORM 實現,很多程度上簡化了 DAO 層的編碼功能。
- 可以很方便的進行數據庫的移植工作。
- 提供了緩存機制,是程序執行更改的高效。
114. 什么是 ORM 框架?
ORM(Object Relation Mapping)對象關系映射,是把數據庫中的關系數據映射成為程序中的對象。
使用 ORM 的優點:提高了開發效率降低了開發成本、開發更簡單更對象化、可移植更強。
115. hibernate 中如何在控制台查看打印的 SQL 語句?
在 Config 里面把 hibernate. show_SQL 設置為 true 就可以。但不建議開啟,開啟之后會降低程序的運行效率。
116. hibernate 有幾種查詢方式?
三種:hql、原生 SQL、條件查詢 Criteria。
117. hibernate 實體類可以被定義為 final 嗎?
實體類可以定義為 final 類,但這樣的話就不能使用 hibernate 代理模式下的延遲關聯提供性能了,所以不建議定義實體類為 final。
118. 在 hibernate 中使用 Integer 和 int 做映射有什么區別?
Integer 類型為對象,它的值允許為 null,而 int 屬於基礎數據類型,值不能為 null。
119. hibernate 是如何工作的?
- 讀取並解析配置文件。
- 讀取並解析映射文件,創建 SessionFactory。
- 打開 Session。
- 創建事務。
- 進行持久化操作。
- 提交事務。
- 關閉 Session。
- 關閉 SessionFactory。
120. get()和 load()的區別?
- 數據查詢時,沒有 OID 指定的對象,get() 返回 null;load() 返回一個代理對象。
- load()支持延遲加載;get() 不支持延遲加載。
121. 說一下 hibernate 的緩存機制?
hibernate 常用的緩存有一級緩存和二級緩存:
一級緩存:也叫 Session 緩存,只在 Session 作用范圍內有效,不需要用戶干涉,由 hibernate 自身維護,可以通過:evict(object)清除 object 的緩存;clear()清除一級緩存中的所有緩存;flush()刷出緩存;
二級緩存:應用級別的緩存,在所有 Session 中都有效,支持配置第三方的緩存,如:EhCache。
122. hibernate 對象有哪些狀態?
- 臨時/瞬時狀態:直接 new 出來的對象,該對象還沒被持久化(沒保存在數據庫中),不受 Session 管理。
- 持久化狀態:當調用 Session 的 save/saveOrupdate/get/load/list 等方法的時候,對象就是持久化狀態。
- 游離狀態:Session 關閉之后對象就是游離狀態。
123. 在 hibernate 中 getCurrentSession 和 openSession 的區別是什么?
- getCurrentSession 會綁定當前線程,而 openSession 則不會。
- getCurrentSession 事務是 Spring 控制的,並且不需要手動關閉,而 openSession 需要我們自己手動開啟和提交事務。
124. hibernate 實體類必須要有無參構造函數嗎?為什么?
hibernate 中每個實體類必須提供一個無參構造函數,因為 hibernate 框架要使用 reflection api,通過調用 ClassnewInstance() 來創建實體類的實例,如果沒有無參的構造函數就會拋出異常。
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()的時候會自動幫你完成查詢工作。這樣做的好處可以有效的防止內存溢出。
Fetch Size 官方相關文檔:http://t. cn/EfSE2g3
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); } }