目錄
一、使用方面的問題
1、什么是MyBatis?
2、為什么說MyBatis是半ORM框架?與Hibernate有哪些不同?
3、MyBatis的優缺點:略
4、#{ } 和 ${ } 的區別是什么?
5、MyBatis是怎么解決物體類中的屬性名和表中的欄位名不一樣的問題?
6、如何在Mapper中傳遞多個引數?
7、MyBatis的介面系結是什么?有哪些系結方式?
8、在MyBatis中使用Mapper介面開發時有哪些要求?
二、原始碼方面的問題
1、MyBatis面向Mapper介面編程的作業原理是什么?
2、MyBatis中Mapper介面中的方法支持多載么?
3、MyBatis中動態SQL的執行原理是什么?
4、MyBatis的作業原理是什么?
4.1 作業原理圖解
4.2 MyBatis的執行流程大概可以分為八個步驟,分別是:
5、MyBatis的快取是什么?
6、MyBatis中一對一查詢、一對多查詢是怎么實作的
6.1 一對一關聯查詢舉例如下:
6.2 一對多關聯查詢舉例如下:
7、MyBatis的分頁原理是什么
8、MyBatis的動態SQL是什么?主要標簽有哪些?
9、MyBatis用到了哪些設計模式?
10、MyBatis中都有哪些Executor執行器?它們之間的區別是什么?
11、MyBatis的Xml映射檔案和MyBatis內部資料結構之間的映射關系?
99、參考
一、使用方面的問題
1、什么是MyBatis?
- MyBatis是一個半ORM框架,它內部封裝了JDBC,開發的時候只需要關注SQL陳述句本身就可以了,我們不需要花太多精力去處理原生的JDBC那一套流程,比如 加載驅動、創建connection連接、創建statement、創建SqlSesssionFactory、創建SQLSession等一系列比較繁雜且重復性比較高的程序,
- MyBatis可以使用 SQL映射檔案XML的方式 或者 注解的方式 來配置映射資訊,將Java物件映射成資料庫當中的記錄,或者將資料庫當中的記錄反映射成Java物件,
- MyBatis也支持動態SQL,它是通過Java物件和statement物件當中SQL的動態引數進行映射從而生成最終要執行的SQL陳述句,最后由MyBatis框架執行SQL并將結果映射為Java物件回傳的,
2、為什么說MyBatis是半ORM框架?與Hibernate有哪些不同?
- ORM,它的意思是指物件和關系之間的映射,包括【物件->關系的映射】和【關系->物件的映射】兩個方面,
- Hibernate是個完整的ORM框架,它實作了這兩個方面的功能,而MyBatis只完成了【關系->物件的映射】,準確地說,MyBatis是SQL映射框架而不是ORM框架,因為它僅有欄位映射,物件資料以及物件的實際關系仍然需要通過手寫SQL來實作,
- MyBatis可以直接撰寫原生SQL,可嚴格控制SQL的執行性能,靈活度比較高,Hibernate只能通過撰寫HQL來實作資料庫查詢,
3、MyBatis的優缺點:略
4、#{ } 和 ${ } 的區別是什么?
- #{ } 是預編譯處理當中的一個占位符,MyBatis在處理 #{ } 的時候,會將SQL中的 #{ } 替換成 ? ,然后,再呼叫PreparedStatement物件的set方法進行賦值,由此來防止SQL注入的問題,使用預編譯的優勢在于,一次編譯,多次運行,省去了邊執行邊決議優化的程序,
- ${ } 是單純的字串文本替換,MyBatis在處理 ${ } 的時候,只是簡單的把 ${ } 替換為變數的值而已,由此會造成SQL注入,帶來不必要的風險,
- 大部分情況下,我們都是使用 #{ } 來處理業務,但是,針對一些特殊的情況,比如 通過一個“變化的欄位”做排序等,也可以使用 ${ } ,
#{parm} 傳入的引數會被當成一個字串,會對自動傳入的資料加一個雙引號,舉例:select * from table where name = #{param},則決議成的sql為:select * from table where name = "id"
${param} 傳入的引數會被當成SQL陳述句中的一部分,舉例:order by ${param},則決議成的sql為:order by id
5、MyBatis是怎么解決物體類中的屬性名和表中的欄位名不一樣的問題?
在MyBatis中,關于物體類和表中欄位名不一致的問題,主要有兩種解決方式:
(1)第一種是使用 <resultMap> 標簽,逐一定義列名和物體類物件屬性名之間的映射關系,
<resultMap id="orderResultMap" type="com.wind.OrderEntity">
<!–用id標簽來映射主鍵欄位,用result屬性來映射非主鍵欄位–>
<!–其中,用property為物體類屬性名,column為資料表中的屬性–>
<id property="id" column="user_id">
<result property="no" column="user_no"/>
</reslutMap>
(2)第二種是使用在SQL中定義列的別名,將列的別名與物體類物件的屬性名一一對應起來,比如 T_Name as name,物體類物件的屬性名一般是小寫,但是列名不區分大小寫,MyBatis也會忽略列名的大小寫,
<select id="selectUserById" parameterType="java.lang.Integer" resultetype="com.wind.UserEntity">
select user_id as id, user_no as no from Test where user_id = #{id}
</select>
6、如何在Mapper中傳遞多個引數?
(1)第一種是使用 @param 注解的方式,比如:
user selectUser(@param("username") string username, @param("password") string password);
(2)第二種是使用Java物件的方式,此時,在Java物件中可以有多個屬性,每一個屬性其實都是一個引數,這樣也可以實作在Mapper中傳遞多個引數,
(3)第三種是使用map集合的方式,此時,需要使Mapper介面方法的輸入引數型別和mapper.xml中定義的每個SQL的parameterType的型別都相同,
7、MyBatis的介面系結是什么?有哪些系結方式?
- 介面系結,就是在MyBatis中定義介面,然后把介面里面的方法和SQL陳述句系結,我們直接呼叫介面中的方法就可以操作資料庫了,這樣比起直接使用SqlSession物件提供的原生態的方法,更加靈活與簡單,
- 介面系結主要有兩種實作方式:(1)第一種是通過注解系結,也就是說 在介面的方法上面加上 @Select、@Update 等注解,注解里面包含SQL陳述句來進行系結,這種方式可以省去SQL的 xml 映射檔案,對于簡單的SQL來說比較適用,后期維護比較困難,平時在業務中基本不怎么使用,(2)第二種是通過在SQL的xml 映射檔案里面寫SQL來進行系結, 在這種情況下,要指定 xml 映射檔案里面的 namespace 引數必須為介面的全類名,不管是SQL簡單還是復雜,xml 檔案的方式 都比較簡單高效,也是最常用的,
8、在MyBatis中使用Mapper介面開發時有哪些要求?
- 一般情況下,在日常開發的時候,會遵循一個mapper.xml映射檔案對應于一張表的增刪改查,
- 其中,mapper.xml映射檔案中的namespace屬性,必須要定義為對應的Mapper介面的全類名,以此來標識一個mapper級別的二級快取,
- Mapper介面中的方法名要和mapper.xml中定義的每個SQL陳述句的id屬性相同,
- Mapper介面中的方法的輸入引數型別要和mapper.xml中定義的每個SQL陳述句的parameterType的型別相同,
- Mapper介面中的方法的輸出引數型別要和mapper.xml中定義的每個SQL陳述句的resultType的型別相同,或者使用resultMap也行,
二、原始碼方面的問題
1、MyBatis面向Mapper介面編程的作業原理是什么?
- MyBatis是面向Mapper介面編程的,一般情況下,一張表對應于一個SQL映射檔案mapper.xml,一個SQL映射檔案又對應于一個Mapper介面,
- Mapper介面,它是沒有實作類的,當呼叫介面方法的時候,它是采用了JDK的動態代理的方式,先從Configuration配置類MapperRegistry物件中獲取Mapper介面和對應的代理物件工廠MapperProxyFactory,然后利用代理物件工廠MapperProxyFactory創建實際代理類MapperProxy,最后在實際代理類中通過MapperMethod類物件內保存的對應方法的資訊,以及對應的SQL陳述句的資訊進行分析,最終確定對應的增強方法進行呼叫,
2、MyBatis中Mapper介面中的方法支持多載么?
- 在MyBatis中,Mapper介面中的方法不支持多載,
- 在MyBatis原始碼中有這么幾行代碼,我們可以看到,在決議XML組態檔創建Mapper介面對應方法的時候,采用了【 介面全類名+方法名】的方式作為 StrictMap(MappedStatement資料存放的Map集合)的key值,而原始碼中對于StrictMap的put方法進行了判斷,如果存入的資料key已重復,則會拋出例外,所以,Mapper介面中的方法不支持多載,

3、MyBatis中動態SQL的執行原理是什么?
在MyBatis中,動態SQL的執行原理大致可以分為三個階段:初始化階段、代理階段、資料讀寫階段,每個階段都在做不同的事情,
- 初始化階段:通過 XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 來決議XML組態檔中的資訊 存盤到Configuration類中的,
- 代理階段:首先從Configuration配置類MapperRegistry物件中獲取Mapper介面和對應的代理物件工廠資訊,然后再利用代理物件工廠MapperProxyFactory創建實際代理類,最后在MapperProxy類中通過MapperMethod類物件內保存的對應方法的資訊,以及對應的SQL陳述句的資訊進行分析,最終確定對應的增強方法的呼叫,
- 資料讀寫階段:通過四種Executor呼叫四種Handler進行資料查詢和資料封裝回傳,
4、MyBatis的作業原理是什么?
4.1 作業原理圖解

4.2 MyBatis的執行流程大概可以分為八個步驟,分別是:
- 首先會讀取MyBatis的組態檔【mybatis-config.xml】,組態檔中配置了MyBatis的運行時環境資訊,例如資料庫連接資訊等,
- 然后會加載MyBatis的映射檔案【xxxMapper.xml】,映射檔案也就是SQL映射檔案,SQL映射檔案中配置了操作資料庫的SQL陳述句,映射檔案需要在【mybatis-config.xml】中加載,【mybatis-config.xml】中可以加載多個不同的映射檔案,每個映射檔案都對應著資料庫中的一張表,
- 接著會通過MyBatis的組態檔構造出一個全域的會話工廠物件【SqlSessionFactory】,
- 再通過會話工廠物件來創建會話物件【SqlSession】,會話物件中包含了執行SQL陳述句的所有方法,
- 在執行SQL陳述句時,其底層使用的是一個執行器Executor介面來操作資料庫,它將根據會話物件【SqlSession】傳遞過來的引數動態的生成需要執行的SQL陳述句,同時會負責查詢快取的維護,
- 在執行器【Executor】介面的執行方法中有一個MappedStatement物件,這個物件是對映射關系的封裝,包括了輸入引數映射和輸出結果映射,
- 在輸入引數映射中,輸入引數可以是普通的基本資料型別,也可以是POJO型別、或者是Map/List等集合型別,輸入引數映射的程序,類似于原生JDBC對preparedStatement物件設定引數的程序,
- 在輸出結果映射中,輸出結果可以是基本資料型別,也可以是POJO型別、或者是Map/List等集合型別,輸出結果映射的程序,類似于原生JDBC對結果集做決議的程序,
5、MyBatis的快取是什么?

- 在MyBatis中,有一級快取和二級快取之分,默認情況下,它的一級快取是打開的,而且不能關閉;二級快取是關閉的,如果想要使用的話,需要手動開啟,做一些配置就行,
- 一級快取是SqlSession級別的快取,一個sqlSession物件代表著我們的程式和資料庫的一次會話,當MyBatis第一次發出一個SQL查詢的時候,SQL的查詢結果首先會寫入SqlSession的一級快取中,快取使用的資料結構是一個HashMap,HashMap的key=MapperID+offset+limit+Sql+所有的入參,HashMap的value=查詢結果;然后再把查詢結果回傳給用戶,當同一個SqlSession再次發出同樣的SQL查詢的時候,就會直接從一級快取中取出資料,這在一定程度上提高了查詢效率,但是,需要注意的是,如果在兩次查詢中出現了DML SQL(insert、delete、update),則本SqlSession的一級快取就會被清空,下次再去查詢的時候,就會直接從資料庫中查詢,另外,一級快取中最多可以快取1024個SQL的key參考查詢結果,
- 二級快取是Mapper級別的快取,可以跨SqlSession物件,不同的SqlSession物件是可以共享的,因為MyBatis是面向Mapper介面編程的,一般情況下,一張表對應于一個Mapper映射檔案,一個Mapper映射檔案又對應于一個Mapper介面,在一個Mapper映射檔案中我們通常會設定一個namespace屬性,這個namespace屬性就標識了這個Mapper介面,所以,MyBatis是以命名空間為單位創建二級快取的,二級快取,它也是一個HashMap的資料結構,key=MapperID+offset+limit+Sql+所有的入參,value=查詢結果,如果開啟了二級快取,那么快取的查詢順序是怎樣的呢?是這樣的,所有的SQL查詢都會首先看一下是否命中了二級快取,如果命中了則直接從二級快取中取出結果并回傳;如果沒命中,再去看一下是否命中了一級快取,如果命中了則直接從一級快取中取出結果,如果還是沒命中,則再去查詢資料庫,對于查詢結果,也是先寫入一級快取、再寫入二級快取、最后再回傳給用戶的這樣一個流程,
- 同時,MyBatis主要是做ORM的,對于快取它并沒有花費十足的精力,所以,它還提供了一些快取介面以供第三方使用,用來提高快取性能,比如常用的是EnCache快取框架,它是一個純Java的行程內快取框架,性能很高,
- 對于MyBatis的快取更新機制,當某一個作用域(一級快取 sqlSession;二級快取namespace)的進行了寫操作后,也就是 insert、delete、update 等DML操作,默認情況下,該作用域下的所有select查詢快取都將被清除,并重新更新;如果開啟了二級快取,則只根據配置判斷是否重繪,
6、MyBatis中一對一查詢、一對多查詢是怎么實作的
- 在MyBatis中,使用association標簽來解決一對一的關聯查詢,association標簽可用的屬性有:(1)property:物件屬性的名稱(2)javaType:物件屬性的型別(3) column:對應的外鍵欄位名稱(4)select:使用另一個查詢封裝的結果,
- 在MyBatis中,使用collection標簽來解決一對多的關聯查詢,collection標簽可用的屬性有:(1)property:指的是集合屬性的值(2)ofType:指的是集合中元素的型別(3)column:所對應的外鍵欄位名稱(4)select:使用另一個查詢封裝的結果,
6.1 一對一關聯查詢舉例如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wind.repository.StudentRepository">
<resultMap id="studentMap" type="com.wind.entity.StudentEntity">
<result column="Id" property="id"/>
<result column="Name" property="name"/>
<result column="ClassId" property="classId"/>
<result column="Status" property="status"/>
<result column="AddTime" property="addTime"/>
<result column="UpdateTime" property="updateTime"/>
</resultMap>
<resultMap id="studentMap2" type="com.wind.entity.StudentEntity">
<result column="Id" property="id"/>
<result column="Name" property="name"/>
<result column="ClassId" property="classId"/>
<result column="Status" property="status"/>
<result column="AddTime" property="addTime"/>
<result column="UpdateTime" property="updateTime"/>
<association property="classEntity" javaType="classEntity">
<id column="cId" property="id"/>
<result column="cClassName" property="className"/>
<result column="cStatus" property="status"/>
</association>
</resultMap>
<sql id="sql_select">
select Id, Name, ClassId, Status, AddTime, UpdateTime from RUN_Student
</sql>
<select id="queryStudent" parameterType="int" resultMap="studentMap">
<include refid="sql_select"/>
where id = #{id} and status = 1
</select>
<select id="queryStudentWithClass" parameterType="int" resultMap="studentMap2">
select r.Id, r.Name, r.ClassId, r.Status, r.AddTime, r.UpdateTime, c.id as cid, c.ClassName as cClassName, c.Status as cStatus
from RUN_Student r join RUN_Class c on r.classId = c.id
where r.id = #{id}
</select>
</mapper>
6.2 一對多關聯查詢舉例如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wind.repository.ClassRepository">
<resultMap id="classStudentMap" type="com.wind.entity.ClassEntity">
<result column="Id" property="id"/>
<result column="ClassName" property="className"/>
<result column="Status" property="status"/>
<result column="AddTime" property="addTime"/>
<result column="UpdateTime" property="updateTime"/>
<collection property="studentEntities" ofType="com.wind.entity.StudentEntity">
<result column="sId" property="id"/>
<result column="sName" property="name"/>
<result column="sClassId" property="classId"/>
<result column="sStatus" property="status"/>
<result column="sAddTime" property="addTime"/>
<result column="sUpdateTime" property="updateTime"/>
</collection>
</resultMap>
<sql id="sql_select_join_student">
select c.Id, c.ClassName, c.Status, c.AddTime, c.UpdateTime,
s.Id as sId, s.Name as sName, s.ClassId as sClassId, s.Status as sStatus, s.AddTime as sAddTime, s.UpdateTime as sUpdateTime
from RUN_Class c join RUN_Student s on c.Id = s.classId
</sql>
<select id="queryClassByClassId" parameterType="int" resultMap="classStudentMap">
<include refid="sql_select_join_student"/>
where c.id = #{id} and c.status = 1 and s.status =1
</select>
</mapper>
7、MyBatis的分頁原理是什么
(1)在MyBatis中,是使用RowBounds物件進行分頁的,它是針對ResultSet結果集執行的邏輯分頁,而不是物理分頁,
(2)另外,我們可以在SQL內,直接書寫帶有物理分頁的引數來完成物理分頁功能,也可以使用第三方的分頁插件PageHelper來完成物理分頁,
<!-- SQL物理分頁 -->
<select id="queryStudentsBySql" parameterType="map" resultMap="studentmapper">
select * from student limit #{start} , #{end}
</select>
(3)分頁插件的原理(物理分頁):就是使用MyBatis提供的插件介面,來實作自定義插件,在自定義插件的攔截方法內,攔截待執行的SQL,然后根據設定的分頁引數 重寫SQL ,生成帶有分頁陳述句的SQL,最終執行的是重寫之后的SQL,從而實作分頁, 舉例:select * from student,分頁插件攔截SQL后 重寫SQL為:select t.* from (select * from student)t limit 0,10;
8、MyBatis的動態SQL是什么?主要標簽有哪些?
- 原生的JDBC的方法,在組合SQL陳述句的時候需要手動去拼接,稍微不注意就會少了一個空格、一個標點符號等,都會導致系統錯誤,MyBatis的動態SQL,就是為了解決這種問題而產生的,MyBatis的動態SQL陳述句是基于OGNL運算式的,方便在SQL陳述句中實作某些邏輯;也可以使用一些標簽靈活的組合成我們想要的SQL陳述句,從而提高開發效率,
- MyBatis的動態SQL標簽,主要有以下幾類:(1) if 標簽,配合 test 標簽用來做簡單的條件判斷,(2)choose 標簽,配合 when、otherwise 標簽,相當于Java語言中的switch...case陳述句實作分支選擇功能,(3)trim 標簽,對包含的內容加上前綴 prefix、或者后綴 suffix, (4)where 標簽,主要是用來簡化SQL陳述句中where條件判斷的,能智能的處理 and、or,不用擔心有多余的 and 或者 or 導致語法錯誤,(5)set 標簽,主要用來做資料update的時候,(6)foreach 標簽,主要用在 Mybatis in 陳述句中,
9、MyBatis用到了哪些設計模式?
- 日志模塊:代理模式、配接器模式
- 資料源模塊:代理模式、工廠模式
- 快取模塊:裝飾器模式
- 初始化階段:建造者模式
- 代理階段:策略模式
- 資料讀寫階段:模板模式
- 插件化開發:責任鏈模式
10、MyBatis中都有哪些Executor執行器?它們之間的區別是什么?

- BaseExecutor:基礎抽象類,實作了Executor介面的大部分方法,主要提供了快取管理和事務管理的能力,使用了模板模式,doUpdate、doQuery、doQueryCursor 等方法的具體實作交給不同的子類去實作,
- CachingExecutor:直接實作Executor介面,使用裝飾器模式提供二級快取能力,先從二級快取中查詢,快取沒有命中再從資料庫中查詢,最后將結果添加到快取中再回傳給用戶,如果在xml檔案中配置了<cache/>節點,則會創建 CachingExecutor,
- BatchExecutor:BaseExecutor的具體子類實作,在doUpdate方法中,提供批量執行多條SQL陳述句的能力,
- SimpleExecutor:BaseExecutor的具體子類實作,且為默認配置,在doQuery方法中使用PrepareStatement物件訪問資料庫,每次訪問都要創建新的PrepareStatement物件,
- ReuseExecutor:BaseExecutor的具體子類實作,與SimpleExecutor不同的是,在doQuery方法中,使用預編譯PrepareStatement物件訪問資料庫,訪問時,會重用快取中的statement物件,而不是每次都創建新的PrepareStatement,
11、MyBatis的Xml映射檔案和MyBatis內部資料結構之間的映射關系?



- MyBatis會將所有Xml配置資訊都封裝到 All-In-One 重量級物件Configuration類物件中,
- 在Xml映射檔案中,<resultMap>標簽會被決議為ResultMap物件,其每個子元素會被決議為ResultMapping物件,
- 每一個<select>、<insert>、<update>、<delete>標簽均會被決議為MappedStatement物件,標簽內的SQL都會被決議為BoundSql 物件,然后再和資料庫打交道,
99、參考
(1)https://blog.csdn.net/cmm0401/article/details/106875187
(2)https://zhuanlan.zhihu.com/p/269260414
(3)https://blog.csdn.net/cmm0401/article/details/111768743
(4)MyBatis面試題集合:https://thinkwon.blog.csdn.net/article/details/101292950
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/280943.html
標籤:其他
