MyBatis是一個半ORM(對象關係映射)框架,它內部封裝了JDBC,開發時只須要關注SQL語句自己,不須要花費精力去加載驅動、建立鏈接、建立Statement等繁雜過程。程序員直接編寫原生態sql,能夠嚴格控制sql執行性能,靈活度高。java
Mybatis可使用XML或註解來配置和映射原生信息,將POJO映射成數據庫中的記錄,避免幾乎全部JDBC代碼和手動設置參數以及得到結果集。mysql
經過xml文件或註解的方式將要執行的各類statement配置起來,並經過java對象和
statement
中sql的動態參數進行映射生成最終執行的sql語句,最後由Mybatis框架執行sql並將返回結果映射爲java對象並返回。(從執行sql到返回result的過程)。
MyBatis專一於SQL自己,是一個足夠靈活的DAO層解決方案。程序員
對性能的要求很高,或者對需求變化較多的項目,如互聯網項目,MyBatis將是不錯的選擇。面試
Mybatis不徹底是一個ORM框架,由於Mybatis須要程序員本身編寫SQL語句。sql
Mybatis直接編寫原生態SQL,能夠嚴格控制SQL執行性能,靈活度高,很是適合對關係數據模型要求不高的軟件開發,由於這類軟件需求變化頻繁,一旦需求變化要求迅速輸出成果。可是靈活的前提是Mybatis沒法作到數據庫無關性,若是須要實現支持多種數據庫的軟件,則須要自定義多套SQL映射文件,工做量大。數據庫
Hibernate對象/關係映射能力強,數據無關性好,對於關係模型要求高的軟件,若是用Hibernate開發能夠節省不少代碼,提升效率。編程
MyBatis使用RowBounds
對象進行分頁,它是針對ResultSet
結果集執行的內存分頁而非物理分頁。能夠在SQL內直接書寫帶有物理分頁的參數來完成分頁功能。也可使用分頁插件來完成物理分頁。緩存
分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截執行的SQL,而後從新SQL,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。markdown
第一種是使用標籤:逐必定義數據庫列明和對象屬性名之間的映射關係。mybatis
第二種是使用SQL列的別名功能:將列的別名書寫爲對象屬性名。
有了列名和屬性名的映射關係後,Mybatis經過反射建立對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性是沒法賦值的。
Mybatis動態SQL能夠在xml映射文件內,以標籤的形式編寫動態sql,執行原理是根據表達式的值完成邏輯判斷並動態拼接SQL的功能。
Mybatis提供了9種動態SQL標籤:
trim|where|set|foreach|if|choose|when|otherwise|bind
還有不少其餘的標籤,<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上動態sql的9個標籤,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中<sql>爲sql片斷標籤,經過<include>標籤引入sql片斷,<selectKey>爲不支持自增的主鍵生成策略標籤。
不一樣的XML映射文件,若是配置了namespace
,那麼id
能夠重複;若是沒有配置namespace
,那麼id不能重複;
緣由是namespace+id
是做爲Map
的key
使用的,若是沒有namespace
,就剩下id
,那麼,id
重複會致使數據互相覆蓋。有了namespace
,天然id
就能夠重複,namespace
不一樣,那麼namespace+id
天然也就會不一樣。
Hibernate屬於全自動的ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,能夠根據對象關係模型直接獲取,因此它是全自動的。而Mybatis在查詢關聯對象或關聯集合對象時,須要手動編寫SQL來完成,因此稱之爲半自動ORM映射工具。
有聯合查詢和嵌套查詢,聯合查詢是幾個表聯合查詢,只查詢一次,經過在resultMap
裏配置association
節點配置一對一的類就能夠完成。
嵌套查詢是先查詢一個表,根據這個表裏的結果的外鍵id,再去另外一表裏查詢數據,也是經過association
配置,可是另外一個表的查詢經過select
屬性配置。
有聯合查詢和嵌套查詢。聯合查詢是幾個表聯合查詢,只查詢一次,經過在resultMap
裏面的collection
節點配置一對多的類就能夠完成;嵌套查詢是先查詢一個表,再去另外一表裏查詢數據,也是經過配置collection
,可是另外一表的查詢經過select
節點配置。
<resultMap id\="惟一的標識" type\="映射的pojo對象"\> <id column\="表的主鍵字段或查詢語句中的別名字段" jdbcType\="字段類型" property\="映射pojo對象的主鍵屬性" /> <result column\="表的一個字段(能夠爲任意表的一個字段)" jdbcType\="字段類型" property\="映射到pojo對象的一個屬性"/> <collection property\="pojo的集合屬性" ofType\="集合中的pojo對象"\> <id column\="集合中pojo對象對應的表的主鍵字段" jdbcType\="字段類型" property\="集合中pojo對象的主鍵屬性" /> <result column\="能夠爲任意表的字段" jdbcType\="字段類型" property\="集合中的pojo對象的屬性" /> </collection\> </resultMap\>
<!-- 一對多關聯查詢 --> <!-- 【resultMap:將整個查詢結果映射到com.mybatis.pojo.User這個實體中】 【id:惟一的標識,後面的select語句中resultMap的值】 【type:映射的pojo對象,映射到com.mybatis.pojo.User這個實體中】 【autoMapping:autoMapping="true"自動映射,放在id或type後面】 結構:<resultMap id="惟一的標識" type="映射的pojo對象"> \--> <resultMap type\="com.mybatis.pojo.User" id\="userMap"\> <!-- 【id:查詢列中的惟一標識,User中的惟一標識】 結構:<id column="表的主鍵字段或查詢語句中的別名字段" jdbcType="字段類型" property="映射pojo對象的主鍵屬性" /> 【column:與數據庫表對應的字段,property:與實體對應的屬性,jdbcType能夠不用寫會自動映射】 結構:<result column="表的一個字段" jdbcType="字段類型" property="映射到pojo對象的一個屬性"/> 【假如數據庫中t\_User表中的主鍵爲user\_id,而實體屬性名稱爲userId,則這個配置應爲 <id column="user\_id" property="userId"/>,result也是同樣,相似hibernate實體映射文件配置】 \--> <id column\="id" property\="id"/> <result column\="username" property\="username"/> <result column\="password" property\="password"/> <result column\="nickname" property\="nickname"/> <result column\="email" property\="email"/> <result column\="email" property\="email"/> <!-- 【collection:對關聯查詢到的多條記錄映射到集合中】 【property:將關聯查詢到的多條記錄映射到com.mybatis.pojo.User類的roles屬性中】 【ofType:指定映射的集合屬性中pojo的類型,roles屬於com.mybatis.pojo.Role對象】 【autoMapping:autoMapping="true"自動映射,放在property或ofType後面】 結構:<collection property="pojo的集合屬性" ofType="集合中的pojo對象"> \--> <collection property\="roles" ofType\="com.mybatis.pojo.Role"\> <!-- 【Role實體映射對應t\_role表】 【id:集合中pojo對象對應的表的主鍵字段】 結構:<id column="集合中pojo對象對應的表的主鍵字段" jdbcType="字段類型" property="集合中pojo對象的主鍵屬性" /> 【column:與數據庫表對應的字段,property:與實體對應的屬性】 結構:<result column="能夠爲任意表的字段" jdbcType="字段類型" property="集合中的pojo對象的屬性" /> \--> <id column\="roleid" property\="id"/> <result column\="name" property\="name"/> <result column\="description" property\="description"/> <result column\="createtime" property\="createtime"/> </collection\> </resultMap\>
](file://C:/Users/10224683/Pictures/markdown/ResultMap%E6%98%A0%E5%B0%84.png?lastModify=1578624600)
Mybatis僅僅支持association關聯對象和collection關聯集合對象的延遲加載。
association指的就是一對一,collection指的就是一對多查詢。在Mybatis配置文件中,能夠配置是否啓用延遲加載:
lazyLoadingEnable=ture|false
它的原理是,使用CGLIB建立目標對象的代理對象,當調用目標方法時,進入攔截器方法,好比調用a.getB().getName()
,攔截器invoke()
方法發現a.getB()
是null值,那麼就會單獨發送事先保存好的查詢關聯B對象的SQL,把B查詢上來,而後調用a.setB(b)
,因而a的對象b屬性就有值了,接着完成a.getB().getName()
方法的調用。這就是延遲加載的基本原理。
固然,不光是Mybatis,幾乎全部的包括Hibernate,支持延遲加載的原理都是樣的。
基於PerpetualCache
的HashMap
本地緩存,其存儲做用域爲Session,當Session flush或close以後,該Session中全部Cache就將清空,默認打開一級緩存。
二級緩存與一級緩存機制相同,默認也是採用PerpetualCache
的HashMap
存儲,不一樣在於其存儲做用域爲Mapper(namespace)
,並能夠自定義存儲源,如Ehcache
。默認不打開二級緩存,須要開啓二級緩存,使用二級緩存屬性類須要實現Serializable
序列化接口(可用來保存對象狀態)。可在它的映射文件中配置。
在MyBatis配置文件中:
<settings\> <!--開啓二級緩存--> <setting name\="cacheEnabled" value\="true"/> </settings\>
在須要開啓二級緩存的mapper.xml中加入caceh標籤
<cache><cache/>
讓使用二級緩存的POJO類實現Serializable接口
public class User implements Serializable {}
對於緩存更新機制,當某一個做用域(一級緩存Session、二級緩存Namespace)進行了C/U/D操做後,默認該做用域下全部select中的緩存將被清除。
#{}
和${}
的區別是什麼?是文件中的變量佔位符,它能夠用於標籤屬性值和內部,屬於靜態文本替換,好比{driver}會被靜態替換爲com.mysql.jdbc.Driver
。#{}是sql的參數佔位符,Mybatis會將sql中的#{}替換爲?號,在sql執行前會使用PreparedStatement
的參數設置方法,按序給sql的?號佔位符設置參數值,好比ps.setInt(0, parameterValue)
,#{item.name}
的取值方式爲使用反射從參數對象中獲取item對象的name屬性值,至關於param.getItem().getName()
。
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執行結果返回。
string wildcardname \= 「%smi%」; list<name\> names \= mapper.selectlike(wildcardname);
<select id\=」selectlike」\> select \* from foo where bar like #{value} </select\>
string wildcardname \= 「smi」; list<name\> names \= mapper.selectlike(wildcardname);
<select id\=」selectlike」\> select \* from foo where bar like "%"#{value}"%" </select\>
參考
(1)Mybatis面試十五問》