MyBatis簡介
- MyBatis是什麼?
- ORM是什麼
- 為什麼說Mybatis是半自動ORM映射工具?它與全自動的差別在哪裡?
- 傳統JDBC開發存在的問題
- JDBC程式設計有哪些不足之處,MyBatis是如何解決這些問題的?
- Mybatis優缺點
- MyBatis架構适用場景
- Hibernate 和 MyBatis 的差別
MyBatis的解析和運作原理
- MyBatis程式設計步驟是什麼樣的?
- 請說說MyBatis的工作原理
- MyBatis的功能架構是怎樣的
- MyBatis的架構架構設計是怎麼樣的
- 為什麼需要預編譯
- Mybatis都有哪些Executor執行器?它們之間的差別是什麼?
- Mybatis中如何指定使用哪一種Executor執行器?
- Mybatis是否支援延遲加載?如果支援,它的實作原理是什麼?
映射器
- #{}和${}的差別
- 模糊查詢like語句該怎麼寫
- 在mapper中如何傳遞多個參數
- Mybatis如何執行批量操作
- 如何擷取生成的主鍵
- 當實體類中的屬性名和表中的字段名不一樣 ,怎麼辦
- Mapper 編寫有哪幾種方式?
- 什麼是MyBatis的接口綁定?有哪些實作方式?
- 使用MyBatis的mapper接口調用時有哪些要求?
- 最佳實踐中,通常一個Xml映射檔案,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什麼?Dao接口裡的方法,參數不同時,方法能重載嗎
- Mybatis的Xml映射檔案中,不同的Xml映射檔案,id是否可以重複?
- 簡述Mybatis的Xml映射檔案和Mybatis内部資料結構之間的映射關系?
- Mybatis是如何将sql執行結果封裝為目标對象并傳回的?都有哪些映射形式?
- Xml映射檔案中,除了常見的select|insert|updae|delete标簽之外,還有哪些标簽?
- Mybatis映射檔案中,如果A标簽通過include引用了B标簽的内容,請問,B标簽能否定義在A标簽的後面,還是說必須定義在A标簽的前面?
進階查詢
- MyBatis實作一對一,一對多有幾種方式,怎麼操作的?
- Mybatis是否可以映射Enum枚舉類?
動态SQL
- Mybatis動态sql是做什麼的?都有哪些動态sql?能簡述一下動态sql的執行原理不?
插件子產品
- Mybatis是如何進行分頁的?分頁插件的原理是什麼?
- 簡述Mybatis的插件運作原理,以及如何編寫一個插件。
緩存
- Mybatis的一級、二級緩存

MyBatis簡介
MyBatis是什麼?
MyBatis 是一款優秀的持久層架構,一個半 ORM(對象關系映射)架構,它支援定制化 SQL、存儲過程以及進階映射。
MyBatis 避免了幾乎所有的 JDBC 代碼和手動設定參數以及擷取結果集。
MyBatis 可以使用簡單的 XML 或注解來配置和映射原生類型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 對象)為資料庫中的記錄。
ORM是什麼
ORM(Object Relational Mapping),對象關系映射,是一種為了解決關系型資料庫資料與簡單Java對象(POJO)的映射關系的技術。
簡單的說,ORM是通過使用描述對象和資料庫之間映射的中繼資料,将程式中的對象自動持久化到關系型資料庫中。
為什麼說Mybatis是半自動ORM映射工具?它與全自動的差別在哪裡?
Hibernate屬于全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關系模型直接擷取,是以它是全自動的。
而Mybatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,是以,稱之為半自動ORM映射工具。
傳統JDBC開發存在的問題
- 頻繁建立資料庫連接配接對象、釋放,容易造成系統資源浪費,影響系統性能。可以使用連接配接池解決這個問題。但是使用jdbc需要自己實作連接配接池。
- sql語句定義、參數設定、結果集處理存在寫死。實際項目中sql語句變化的可能性較大,一旦發生變化,需要修改java代碼,系統需要重新編譯,重新釋出。不好維護。
- 使用preparedStatement向占有位符号傳參數存在寫死,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
- 結果集處理存在重複代碼,處理麻煩。如果可以映射成Java對象會比較友善。
JDBC程式設計有哪些不足之處,MyBatis是如何解決這些問題的?
1、資料庫連結建立、釋放頻繁造成系統資源浪費進而影響系統性能,如果使用資料庫連接配接池可解決此問題。
解決:在mybatis-config.xml中配置資料連結池,使用連接配接池管理資料庫連接配接。
2、Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
解決:将Sql語句配置在XXXXmapper.xml檔案中與java代碼分離。
3、向sql語句傳參數麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數一一對應。
解決: Mybatis自動将java對象映射至sql語句。
4、對結果集解析麻煩,sql變化導緻解析代碼變化,且解析前需要周遊,如果能将資料庫記錄封裝成pojo對象解析比較友善。
解決:Mybatis自動将sql執行結果映射至java對象。
Mybatis優缺點
優點
與傳統的資料庫通路技術相比,ORM有以下優點:
- 基于SQL語句程式設計,相當靈活,不會對應用程式或者資料庫的現有設計造成任何影響,SQL寫在XML裡,解除sql與程式代碼的耦合,便于統一管理;提供XML标簽,支援編寫動态SQL語句,并可重用
- 與JDBC相比,減少了50%以上的代碼量,消除了JDBC大量備援的代碼,不需要手動開關連接配接
- 很好的與各種資料庫相容(因為MyBatis使用JDBC來連接配接資料庫,是以隻要JDBC支援的資料庫MyBatis都支援)
- 提供映射标簽,支援對象與資料庫的ORM字段關系映射;提供對象關系映射标簽,支援對象關系元件維護
- 能夠與Spring很好的內建
缺點
- SQL語句的編寫工作量較大,尤其當字段多、關聯表多時,對開發人員編寫SQL語句的功底有一定要求
- SQL語句依賴于資料庫,導緻資料庫移植性差,不能随意更換資料庫
MyBatis架構适用場景
- MyBatis專注于SQL本身,是一個足夠靈活的DAO層解決方案。
- 對性能的要求很高,或者需求變化較多的項目,如網際網路項目,MyBatis将是不錯的選擇。
Hibernate 和 MyBatis 的差別
相同點
都是對jdbc的封裝,都是持久層的架構,都用于dao層的開發。
不同點
映射關系
- MyBatis 是一個半自動映射的架構,配置Java對象與sql語句執行結果的對應關系,多表關聯關系配置簡單
- Hibernate 是一個全表映射的架構,配置Java對象與資料庫表的對應關系,多表關聯關系配置複雜
SQL優化和移植性
- Hibernate 對SQL語句封裝,提供了日志、緩存、級聯(級聯比 MyBatis 強大)等特性,此外還提供 HQL(Hibernate Query Language)操作資料庫,資料庫無關性支援好,但會多消耗性能。如果項目需要支援多種資料庫,代碼開發量少,但SQL語句優化困難。
- MyBatis 需要手動編寫 SQL,支援動态 SQL、處理清單、動态生成表名、支援存儲過程。開發工作量相對大些。直接使用SQL語句操作資料庫,不支援資料庫無關性,但sql語句優化容易。
開發難易程度和學習成本
- Hibernate 是重量級架構,學習使用門檻高,适合于需求相對穩定,中小型的項目,比如:辦公自動化系統
- MyBatis 是輕量級架構,學習使用門檻低,适合于需求變化頻繁,大型的項目,比如:網際網路電子商務系統
總結
MyBatis 是一個小巧、友善、高效、簡單、直接、半自動化的持久層架構,
Hibernate 是一個強大、友善、高效、複雜、間接、全自動化的持久層架構。
MyBatis的解析和運作原理
MyBatis程式設計步驟是什麼樣的?
1、 建立SqlSessionFactory
2、 通過SqlSessionFactory建立SqlSession
3、 通過sqlsession執行資料庫操作
4、 調用session.commit()送出事務
5、 調用session.close()關閉會話
請說說MyBatis的工作原理
在學習 MyBatis 程式之前,需要了解一下 MyBatis 工作原理,以便于了解程式。
MyBatis 的工作原理如下圖
1)讀取 MyBatis 配置檔案:mybatis-config.xml 為 MyBatis 的全局配置檔案,配置了 MyBatis 的運作環境等資訊,例如資料庫連接配接資訊。
2)加載映射檔案。映射檔案即 SQL 映射檔案,該檔案中配置了操作資料庫的 SQL 語句,需要在 MyBatis 配置檔案 mybatis-config.xml 中加載。mybatis-config.xml 檔案可以加載多個映射檔案,每個檔案對應資料庫中的一張表。
3)構造會話工廠:通過 MyBatis 的環境等配置資訊建構會話工廠 SqlSessionFactory。
4)建立會話對象:由會話工廠建立 SqlSession 對象,該對象中包含了執行 SQL 語句的所有方法。
5)Executor 執行器:MyBatis 底層定義了一個 Executor 接口來操作資料庫,它将根據 SqlSession 傳遞的參數動态地生成需要執行的 SQL 語句,同時負責查詢緩存的維護。
6)MappedStatement 對象:在 Executor 接口的執行方法中有一個 MappedStatement 類型的參數,該參數是對映射資訊的封裝,用于存儲要映射的 SQL 語句的 id、參數等資訊。
7)輸入參數映射:輸入參數類型可以是 Map、List 等集合類型,也可以是基本資料類型和 POJO 類型。輸入參數映射過程類似于 JDBC 對 preparedStatement 對象設定參數的過程。
8)輸出結果映射:輸出結果類型可以是 Map、 List 等集合類型,也可以是基本資料類型和 POJO 類型。輸出結果映射過程類似于 JDBC 對結果集的解析過程。
MyBatis的功能架構是怎樣的
我們把Mybatis的功能架構分為三層:
- API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱資料庫。接口層一接收到調用請求就會調用資料處理層來完成具體的資料處理。
- 資料處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次資料庫操作。
- 基礎支撐層:負責最基礎的功能支撐,包括連接配接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,将他們抽取出來作為最基礎的元件。為上層的資料處理層提供最基礎的支撐。
MyBatis的架構架構設計是怎麼樣的
這張圖從上往下看。MyBatis的初始化,會從mybatis-config.xml配置檔案,解析構造成Configuration這個類,就是圖中的紅框。
(1)加載配置:配置來源于兩個地方,一處是配置檔案,一處是Java代碼的注解,将SQL的配置資訊加載成為一個個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在記憶體中。
(2)SQL解析:當API接口層接收到調用請求時,會接收到傳入SQL的ID和傳入對象(可以是Map、JavaBean或者基本資料類型),Mybatis會根據SQL的ID找到對應的MappedStatement,然後根據傳入參數對象對MappedStatement進行解析,解析後可以得到最終要執行的SQL語句和參數。
(3)SQL執行:将最終得到的SQL和參數拿到資料庫進行執行,得到操作資料庫的結果。
(4)結果映射:将操作資料庫的結果按照映射的配置進行轉換,可以轉換成HashMap、JavaBean或者基本資料類型,并将最終結果傳回。
為什麼需要預編譯
1. 定義:
SQL 預編譯指的是資料庫驅動在發送 SQL 語句和參數給 DBMS 之前對 SQL 語句進行編譯,這樣 DBMS 執行 SQL 時,就不需要重新編譯。
2.為什麼需要預編譯
JDBC 中使用對象 PreparedStatement 來抽象預編譯語句,使用預編譯。預編譯階段可以優化 SQL 的執行。預編譯之後的 SQL 多數情況下可以直接執行,DBMS 不需要再次編譯,越複雜的SQL,編譯的複雜度将越大,預編譯階段可以合并多次操作為一個操作。同時預編譯語句對象可以重複利用。把一個 SQL 預編譯後産生的 PreparedStatement 對象緩存下來,下次對于同一個SQL,可以直接使用這個緩存的 PreparedState 對象。Mybatis預設情況下,将對所有的 SQL 進行預編譯。
Mybatis都有哪些Executor執行器?它們之間的差別是什麼?
Mybatis有三種基本的Executor執行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
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中如何指定使用哪一種Executor執行器?
在Mybatis配置檔案中,在設定(settings)可以指定預設的ExecutorType執行器類型,也可以手動給DefaultSqlSessionFactory的建立SqlSession的方法傳遞ExecutorType類型參數,如SqlSession openSession(ExecutorType execType)。
配置預設的執行器。
SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(prepared statements); BATCH 執行器将重用語句并執行批量更新。
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,支援延遲加載的原理都是一樣的。
映射器
#{}和${}的差別
- #{}是占位符,預編譯處理;${}是拼接符,字元串替換,沒有預編譯處理。
- Mybatis在處理#{}時,#{}傳入參數是以字元串傳入,會将SQL中的#{}替換為?号,調用PreparedStatement的set方法來指派。
- Mybatis在處理時 , 是 原 值 傳 入 , 就 是 把 {}時,是原值傳入,就是把時,是原值傳入,就是把{}替換成變量的值,相當于JDBC中的Statement編譯
- 變量替換後,#{} 對應的變量自動加上單引号 ‘’;變量替換後,${} 對應的變量不會加上單引号 ‘’
- #{} 可以有效的防止SQL注入,提高系統安全性;${} 不能防止SQL 注入
- #{} 的變量替換是在DBMS 中;${} 的變量替換是在 DBMS 外
模糊查詢like語句該怎麼寫
(1)’%${question}%’ 可能引起SQL注入,不推薦
(2)"%"#{question}"%" 注意:因為#{…}解析成sql語句時候,會在變量外側自動加單引号’ ',是以這裡 % 需要使用雙引号" ",不能使用單引号 ’ ',不然會查不到任何結果。
(3)CONCAT(’%’,#{question},’%’) 使用CONCAT()函數,推薦
(4)使用bind标簽
在mapper中如何傳遞多個參數
方法1:順序傳參法
#{}裡面的數字代表傳入參數的順序。
這種方法不建議使用,sql層表達不直覺,且一旦順序調整容易出錯。
方法2:@Param注解傳參法
#{}裡面的名稱對應的是注解@Param括号裡面修飾的名稱。
這種方法在參數不多的情況還是比較直覺的,推薦使用。
方法3:Map傳參法
#{}裡面的名稱對應的是Map裡面的key名稱。
這種方法适合傳遞多個參數,且參數易變能靈活傳遞的情況。
方法4:Java Bean傳參法
#{}裡面的名稱對應的是User類裡面的成員屬性。
這種方法直覺,需要建一個實體類,擴充不容易,需要加屬性,但代碼可讀性強,業務邏輯處理友善,推薦使用。
Mybatis如何執行批量操作
使用foreach标簽
foreach的主要用在建構in條件中,它可以在SQL語句中進行疊代一個集合。foreach标簽的屬性主要有item,index,collection,open,separator,close。
- item 表示集合中每一個元素進行疊代時的别名,随便起的變量名;
- index 指定一個名字,用于表示在疊代過程中,每次疊代到的位置,不常用;
- open 表示該語句以什麼開始,常用“(”;
- separator表示在每次進行疊代之間以什麼符号作為分隔符,常用“,”;
- close 表示以什麼結束,常用“)”。
在使用foreach的時候最關鍵的也是最容易出錯的就是collection屬性,該屬性是必須指定的,但是在不同情況下,該屬性的值是不一樣的,主要有一下3種情況:
- 如果傳入的是單參數且參數類型是一個List的時候,collection屬性值為list
- 如果傳入的是單參數且參數類型是一個array數組的時候,collection的屬性值為array
- 如果傳入的參數是多個的時候,我們就需要把它們封裝成一個Map了,當然單參數也可以封裝成map,實際上如果你在傳入參數的時候,在MyBatis裡面也是會把它封裝成一個Map的,map的key就是參數名,是以這個時候collection屬性值就是傳入的List或array對象在自己封裝的map裡面的key
具體用法如下:
使用ExecutorType.BATCH
Mybatis内置的ExecutorType有3種,預設為simple,該模式下它為每個語句的執行建立一個新的預處理語句,單條送出sql;
而batch模式重複使用已經預處理的語句,并且批量執行所有更新語句,顯然batch性能将更優;
但batch模式也有自己的問題,比如在Insert操作時,在事務沒有送出之前,是沒有辦法擷取到自增的id,這在某型情形下是不符合業務要求的
具體用法如下
mapper和mapper.xml如下
如何擷取生成的主鍵
對于支援主鍵自增的資料庫(MySQL)
parameterType 可以不寫,Mybatis可以推斷出傳入的資料類型。如果想要通路主鍵,那麼應當parameterType 應當是java實體或者Map。
這樣資料在插入之後 可以通過Java實體或者Map 來擷取主鍵值。通過 getUserId擷取主鍵
不支援主鍵自增的資料庫(Oracle)
對于像Oracle這樣的資料,沒有提供主鍵自增的功能,而是使用序列的方式擷取自增主鍵。
可以使用
<selectKey>
标簽來擷取主鍵的值,這種方式不僅适用于不提供主鍵自增功能的資料庫,也适用于提供主鍵自增功能的資料庫
<selectKey>
一般的用法
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE">
</selectKey>
複制
keyProperty: selectKey 語句結果應該被設定的目标屬性。如果希望得到多個生成的列,也可以是逗号分隔的屬性名稱清單。
keyColumn: 比對屬性的傳回結果集中的列名稱。如果希望得到多個生成的列,也可以是逗号分隔的屬性名稱清單。
resultType: 結果的類型,MyBatis 通常可以推算出來。MyBatis 允許任何簡單類型用作主鍵的類型,包括字元串。如果希望作用于多個生成的列,則可以使用一個包含期望屬性的 Object 或一個 Map。
order: 值可為BEFORE 或 AFTER。如果是 BEFORE,那麼它會先執行selectKey設定 keyProperty 然後執行插入語句。如果為AFTER則相反。
statementType: 使用何種語句類型,預設PREPARED。 有STATEMENT,PREPARED 和 CALLABLE 語句的映射類型。
<insert id="insertUser" >
<selectKey keyColumn="id" resultType="long" keyProperty="userId" order="BEFORE">
SELECT USER_ID.nextval as id from dual
</selectKey>
insert into user(
user_id,user_name, user_password, create_time)
values(#{userId},#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>
複制
此時會将Oracle生成的主鍵值賦予userId變量。這個userId 就是USER對象的屬性,這樣就可以将生成的主鍵值傳回了。
如果僅僅是在insert語句中使用但是不傳回,此時keyProperty=“任意自定義變量名”,resultType 可以不寫。
Oracle 資料庫中的值要設定為 BEFORE ,這是因為 Oracle中需要先從序列擷取值,然後将值作為主鍵插入到資料庫中。
擴充
如果Mysql 使用selectKey的方式擷取主鍵,需要注意下面兩點:
order : AFTER
擷取遞增主鍵值 :SELECT LAST_INSERT_ID()
當實體類中的屬性名和表中的字段名不一樣 ,怎麼辦
第1種: 通過在查詢的SQL語句中定義字段名的别名,讓字段名的别名和實體類的屬性名一緻。
<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>
複制
第2種: 通過
<resultMap>
來映射字段名和實體類屬性名的一一對應的關系。
<select id="getOrder" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id=#{id}
</select>
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
<!–用id屬性來映射主鍵字段–>
<id property="id" column="order_id">
<!–用result屬性來映射非主鍵字段,property為實體類屬性名,column為資料庫表中的屬性–>
<result property ="orderno" column ="order_no"/>
<result property="price" column="order_price" />
</reslutMap>
複制
Mapper 編寫有哪幾種方式?
第一種:接口實作類繼承 SqlSessionDaoSupport:使用此種方法需要編寫mapper 接口,mapper 接口實作類、mapper.xml 檔案。
(1)在 sqlMapConfig.xml 中配置 mapper.xml 的位置
<mappers>
<mapper resource="mapper.xml 檔案的位址" />
<mapper resource="mapper.xml 檔案的位址" />
</mappers>
複制
(2)定義 mapper 接口
(3)實作類內建 SqlSessionDaoSupport
mapper 方法中可以 this.getSqlSession()進行資料增删改查。
(4)spring 配置
<bean id=" " class="mapper 接口的實作">
<property name="sqlSessionFactory"
ref="sqlSessionFactory"></property>
</bean>
複制
第二種:使用 org.mybatis.spring.mapper.MapperFactoryBean:
(1)在 sqlMapConfig.xml 中配置 mapper.xml 的位置,如果 mapper.xml 和mappre 接口的名稱相同且在同一個目錄,這裡可以不用配置
<mappers>
<mapper resource="mapper.xml 檔案的位址" />
<mapper resource="mapper.xml 檔案的位址" />
</mappers>
複制
(2)定義 mapper 接口:
(3)mapper.xml 中的 namespace 為 mapper 接口的位址
(4)mapper 接口中的方法名和 mapper.xml 中的定義的 statement 的 id 保持一緻
(5)Spring 中定義
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper 接口位址" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
複制
第三種:使用 mapper 掃描器:
(1)mapper.xml 檔案編寫:
mapper.xml 中的 namespace 為 mapper 接口的位址;
mapper 接口中的方法名和 mapper.xml 中的定義的 statement 的 id 保持一緻;
如果将 mapper.xml 和 mapper 接口的名稱保持一緻則不用在 sqlMapConfig.xml中進行配置。
(2)定義 mapper 接口:
注意 mapper.xml 的檔案名和 mapper 的接口名稱保持一緻,且放在同一個目錄
(3)配置 mapper 掃描器:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper 接口包位址
"></property>
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"/>
</bean>
複制
(4)使用掃描器後從 spring 容器中擷取 mapper 的實作對象。
什麼是MyBatis的接口綁定?有哪些實作方式?
接口綁定,就是在MyBatis中任意定義接口,然後把接口裡面的方法和SQL語句綁定,我們直接調用接口方法就可以,這樣比起原來了SqlSession提供的方法我們可以有更加靈活的選擇和設定。
接口綁定有兩種實作方式:
- 通過注解綁定,就是在接口的方法上面加上 @Select、@Update等注解,裡面包含Sql語句來綁定;
- 通過xml裡面寫SQL來綁定, 在這種情況下,要指定xml映射檔案裡面的namespace必須為接口的全路徑名。當Sql語句比較簡單時候,用注解綁定, 當SQL語句比較複雜時候,用xml綁定,一般用xml綁定的比較多。
使用MyBatis的mapper接口調用時有哪些要求?
1、Mapper接口方法名和mapper.xml中定義的每個sql的id相同。
2、Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同。
3、Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同。
4、Mapper.xml檔案中的namespace即是mapper接口的類路徑。
最佳實踐中,通常一個Xml映射檔案,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什麼?Dao接口裡的方法,參數不同時,方法能重載嗎
Dao接口,就是人們常說的Mapper接口,接口的全限名,就是映射檔案中的namespace的值,接口的方法名,就是映射檔案中MappedStatement的id值,接口方法内的參數,就是傳遞給sql的參數。
Mapper接口是沒有實作類的,當調用接口方法時,接口全限名+方法名拼接字元串作為key值,可唯一定位一個MappedStatement,舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace為com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一個<select>、<insert>、<update>、<delete>标簽,都會被解析為一個MappedStatement對象。
Dao接口裡的方法,是不能重載的,因為是全限名+方法名的儲存和尋找政策。
Dao接口的工作原理是JDK動态代理,Mybatis運作時會使用JDK動态代理為Dao接口生成代理proxy對象,代理對象proxy會攔截接口方法,轉而執行MappedStatement所代表的sql,然後将sql執行結果傳回。
Mybatis的Xml映射檔案中,不同的Xml映射檔案,id是否可以重複?
不同的Xml映射檔案,如果配置了namespace,那麼id可以重複;
如果沒有配置namespace,那麼id不能重複;畢竟namespace不是必須的,隻是最佳實踐而已。
原因就是namespace+id是作為Map<String, MappedStatement>的key使用的,如果沒有namespace,就剩下id,那麼,id重複會導緻資料互相覆寫。
有了namespace,自然id就可以重複,namespace不同,namespace+id自然也就不同。
簡述Mybatis的Xml映射檔案和Mybatis内部資料結構之間的映射關系?
答:Mybatis将所有Xml配置資訊都封裝到All-In-One重量級對象Configuration内部。
在Xml映射檔案中,<parameterMap>标簽會被解析為ParameterMap對象,其每個子元素會被解析為ParameterMapping對象。
<resultMap>标簽會被解析為ResultMap對象,其每個子元素會被解析為ResultMapping對象。
每一個<select>、<insert>、<update>、<delete>标簽均會被解析為MappedStatement對象,标簽内的sql會被解析為BoundSql對象。
Mybatis是如何将sql執行結果封裝為目标對象并傳回的?都有哪些映射形式?
第一種是使用
<resultMap>
标簽,逐一定義列名和對象屬性名之間的映射關系。
第二種是使用sql列的别名功能,将列别名書寫為對象屬性名,比如T_NAME AS NAME,對象屬性名一般是name,小寫,但是列名不區分大小寫,Mybatis會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至可以寫成T_NAME AS NaMe,Mybatis一樣可以正常工作。
有了列名與屬性名的映射關系後,Mybatis通過反射建立對象,同時使用反射給對象的屬性逐一指派并傳回,那些找不到映射關系的屬性,是無法完成指派的。
Xml映射檔案中,除了常見的select|insert|updae|delete标簽之外,還有哪些标簽?
還有很多其他的标簽,<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上動态sql的9個标簽,trim|where|set|foreach|if|choose|when|otherwise|bind等,
其中<sql>為sql片段标簽,通過<include>标簽引入sql片段,<selectKey>為不支援自增的主鍵生成政策标簽。
Mybatis映射檔案中,如果A标簽通過include引用了B标簽的内容,請問,B标簽能否定義在A标簽的後面,還是說必須定義在A标簽的前面?
雖然Mybatis解析Xml映射檔案是按照順序解析的,但是,被引用的B标簽依然可以定義在任何地方,Mybatis都可以正确識别。
原理是,Mybatis解析A标簽,發現A标簽引用了B标簽,但是B标簽尚未解析到,尚不存在,此時,Mybatis會将A标簽标記為未解析狀态,然後繼續解析餘下的标簽,包含B标簽,
待所有标簽解析完畢,Mybatis會重新解析那些被标記為未解析的标簽,此時再解析A标簽時,B标簽已經存在,A标簽也就可以正常解析完成了。
進階查詢
MyBatis實作一對一,一對多有幾種方式,怎麼操作的?
有聯合查詢和嵌套查詢。
聯合查詢是幾個表聯合查詢,隻查詢一次,通過在resultMap裡面的association,collection節點配置一對一,一對多的類就可以完成。
嵌套查詢是先查一個表,根據這個表裡面的結果的外鍵id,去再另外一個表裡面查詢資料,也是通過配置association,collection,但另外一個表的查詢通過select節點配置。
Mybatis是否可以映射Enum枚舉類?
Mybatis可以映射枚舉類,不單可以映射枚舉類,Mybatis可以映射任何對象到表的一列上。
映射方式為自定義一個TypeHandler,實作TypeHandler的setParameter()和getResult()接口方法。
TypeHandler有兩個作用,一是完成從javaType至jdbcType的轉換,二是完成jdbcType至javaType的轉換,展現為setParameter()和getResult()兩個方法,分别代表設定sql問号占位符參數和擷取列查詢結果。
動态SQL
Mybatis動态sql是做什麼的?都有哪些動态sql?能簡述一下動态sql的執行原理不?
Mybatis動态sql可以讓我們在Xml映射檔案内,以标簽的形式編寫動态sql,完成邏輯判斷和動态拼接sql的功能,
Mybatis提供了9種動态sql标簽trim|where|set|foreach|if|choose|when|otherwise|bind。
其執行原理為,使用OGNL從sql參數對象中計算表達式的值,根據表達式的值動态拼接sql,以此來完成動态sql的功能。
插件子產品
Mybatis是如何進行分頁的?分頁插件的原理是什麼?
Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的記憶體分頁,而非實體分頁,
可以在sql内直接書寫帶有實體分頁的參數來完成實體分頁功能,也可以使用分頁插件來完成實體分頁。
分頁插件的基本原理是使用Mybatis提供的插件接口,實作自定義插件,在插件的攔截方法内攔截待執行的sql,然後重寫sql,根據dialect方言,添加對應的實體分頁語句和實體分頁參數。
舉例:select * from student,攔截sql後重寫為:select t.* from (select * from student) t limit 0, 10
簡述Mybatis的插件運作原理,以及如何編寫一個插件。
Mybatis僅可以編寫針對ParameterHandler、ResultSetHandler、StatementHandler、Executor這4種接口的插件,
Mybatis使用JDK的動态代理,為需要攔截的接口生成代理對象以實作接口方法攔截功能,每當執行這4種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,當然,隻會攔截那些你指定需要攔截的方法。
實作Mybatis的Interceptor接口并複寫intercept()方法,然後在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可,記住,别忘了在配置檔案中配置你編寫的插件。
緩存
Mybatis的一級、二級緩存
1)一級緩存: 基于 PerpetualCache 的 HashMap 本地緩存,其存儲作用域為 Session,當 Session flush 或 close 之後,該 Session 中的所有 Cache 就将清空,預設打開一級緩存。
2)二級緩存與一級緩存其機制相同,預設也是采用 PerpetualCache,HashMap 存儲,不同在于其存儲作用域為 Mapper(Namespace),并且可自定義存儲源,如 Ehcache。預設不打開二級緩存,要開啟二級緩存,使用二級緩存屬性類需要實作Serializable序列化接口(可用來儲存對象的狀态),可在它的映射檔案中配置<cache/> ;
3)對于緩存資料更新機制,當某一個作用域(一級緩存 Session/二級緩存Namespaces)的進行了C/U/D 操作後,預設該作用域下所有 select 中的緩存将被 clear。
參考:https://blog.csdn.net/ThinkWon/article/details/101292950