mybatis面試題


1、#{} 和 ${} 的區別是什么?
2、當實體類中的屬性名和表中的字段名不⼀樣,怎么辦?
3、XML 映射⽂件中,除了常⻅的 select | insert | update | delete標簽之外,還有哪些標簽?
4、Mybatis 動態 SQL 是做什么的?都有哪些動態 SQL ?能簡述⼀下動態 SQL 的執⾏原理嗎?
5、最佳實踐中,通常⼀個 XML 映射⽂件,都會寫⼀個 Mapper 接⼝與之對應。請問,這個 Mapper 接⼝的⼯作原理是什
么?Mapper 接⼝⾥的⽅法,參數不同時,⽅法能重載嗎?
6、Mapper 接⼝綁定有⼏種實現⽅式,分別是怎么實現的?
7、Mybatis 的 XML Mapper⽂件中,不同的 XML 映射⽂件,id 是否可以重復?
8、如何獲取⾃動⽣成的(主)鍵值?
9、Mybatis 執⾏批量插⼊,能返回數據庫主鍵列表嗎?
10、在 Mapper 中如何傳遞多個參數?
11、Mybatis 是否可以映射 Enum 枚舉類?
12、Mybatis 都有哪些 Executor 執⾏器?它們之間的區別是什么?
13、MyBatis 如何執⾏批量插⼊?
14、介紹 MyBatis 的⼀級緩存和⼆級緩存的概念和實現原理?
15、Mybatis 是否⽀持延遲加載?如果⽀持,它的實現原理是什么?
16、Mybatis 能否執⾏⼀對⼀、⼀對多的關聯查詢嗎?都有哪些實現⽅式,以及它們之間的區別。
17、簡述 Mybatis 的插件運⾏原理?以及如何編寫⼀個插件?
18、Mybatis 是如何進⾏分⻚的?分⻚插件的原理是什么?
19、MyBatis 與 Hibernate 有哪些不同?
20、JDBC 編程有哪些不⾜之處,MyBatis是如何解決這些問題的?
21、Mybatis ⽐ IBatis ⽐較⼤的⼏個改進是什么?
22、Mybatis 映射⽂件中,如果 A 標簽通過 include 引⽤了B標簽的內容,請問,B 標簽能否定義在 A 標簽的后⾯,還是說
必須定義在A標簽的前⾯?
23、簡述 Mybatis 的 XML 映射⽂件和 Mybatis 內部數據結構之間的映射關系?
24、MyBatis的優缺點

 

 

 

1、#{} 和 ${} 的區別是什么?

${}是 Properties ⽂件中的變量占位符,它可以⽤於 XML 標簽屬性值和 SQL 內部,屬於字符串替換。例如將
${driver}會被靜態替換為com.mysql.jdbc.Driver:

1<dataSource type="UNPOOLED">
2<property name="driver" value="${driver}"/>
3<property name="url" value="${url}"/>
4<property name="username" value="${username}"/>
5</dataSource>

${}也可以對傳遞進來的參數原樣拼接在 SQL 中。代碼如下:

1<select id="getSubject3" parameterType="Integer" resultType="Subject">
2SELECT*FROM subject
3WHERE id = ${id}
4</select>

實際場景下,不推薦這么做。因為,可能有 SQL 注⼊的⻛險。

#{} 是 SQL 的參數占位符,Mybatis 會將 SQL 中的 #{} 替換為 ? 號,在 SQL 執⾏前會使⽤ PreparedStatement 的參數設置⽅法,按
序給 SQL 的 ? 號占位符設置參數值,⽐如 ps.setInt(0, parameterValue)。所以,#{} 是預編譯處理,可以有效防⽌ SQL 注⼊,提⾼
系統安全性。
另外,#{} 和 ${} 的取值⽅式⾮常⽅便。例如:#{item.name} 的取值⽅式,為使⽤反射從參數對象中,獲取 item 對象的 name 屬性
值,相當於 param.getItem().getName()。

2、當實體類中的屬性名和表中的字段名不⼀樣,怎么辦?

第一種,通過在查詢的 SQL 語句中定義字段名的別名,讓字段名的別名和實體類的屬性名⼀致。代碼如下:

1<select id="selectOrder" parameterType="Integer" resultType="Order">
2SELECT order_id AS id, order_no AS orderno, order_price AS price 
3FROM orders 
4WHERE order_id = #{id}
5</select>

幾點建議:
  1、數據庫的關鍵字,統一使用大寫,例如:SELECT、AS、FROM、WHERE。
  2、每 5 個查詢字段換一行,保持整齊。
  3、,的后⾯,和=的前后,需要有空格,更加清晰。
  4、SELECT、FROM、WHERE等,單獨一行,高端大氣。


第二種,是第一種的特殊情況。⼤多數場景下,數據庫字段名和實體類中的屬性名差,主要是前者為下划線⻛格,后者為駝峰⻛格。
在這種情況下,可以直接配置如下,實現⾃動的下划線轉駝峰的功能。

1<setting name="logImpl" value="LOG4J"/>
2<setting name="mapUnderscoreToCamelCase" value="true"/>
3</settings>

也就說,約定大於配置。非常推薦!


第三種,通過 <resultMap> 來映射字段名和實體類屬性名的一一對應的關系。代碼如下:

1<resultMap type="me.gacl.domain.Order" id=”OrderResultMap”>
2<!–-⽤ id 屬性來映射主鍵字段-–>
3<id property="id" column="order_id">
4<!–-⽤ result 屬性來映射⾮主鍵字段,property 為實體類屬性名,column 為數據表中的屬性-–>
5<result property="orderNo" column ="order_no"/>
6<result property="price" column="order_price"/>
7</resultMap>
8
9<select id="getOrder" parameterType="Integer" resultMap="OrderResultMap">
10SELECT*
11FROM orders 
12WHERE order_id = #{id}
13</select>

此處 SELECT * 僅僅作為示例只用,實際場景下,千萬千萬千萬不要這么干。用多少字段,查詢多少字段。
相比第一種,第三種的重⽤性會⼀些。


3、XML 映射文件中,除了常見的 select | insert | update | delete標 簽之外,還有哪些標簽?


http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

<cache />標簽,給定命名空間的緩存配置。
<cache-ref /> 標簽,其他命名空間緩存配置的引⽤。
<resultMap />標簽,是最復雜也是最強⼤的元素,⽤來描述如何從數據庫結果集中來加載對象。
<parameterMap />標簽,已廢棄!⽼式⻛格的參數映射。內聯參數是⾸選,這個元素可能在將來被移除,這⾥不會記錄。
<sql />標簽,可被其他語句引⽤的可重⽤語句塊。
<include />標簽,引⽤<sql /> 標簽的語句。
<selectKey /> 標簽,不⽀持⾃增的主鍵⽣成策略標簽

http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html

<if />
<choose />、<when />、<otherwise />
<trim />、<where />、<set />
<foreach />
<bind />

 

4、Mybatis 動態 SQL 是做什么的?都有哪些動態 SQL ?能簡述一下動態 SQL 的執行原理嗎?

 

Mybatis 動態 SQL ,可以讓我們在 XML 映射⽂件內,以 XML 標簽的形式編寫動態 SQL ,完成邏輯判斷和動態拼接 SQL 的功
能。
Mybatis 提供了 9 種動態 SQL 標簽:<if />、<choose />、<when />、<otherwise />、<trim />、<where />、<set />、
<foreach />、<bind /> 。
其執行原理為,使用OGNL 的表達式,從 SQL 參數對象中計算表達式的值,根據表達式的值動態拼接 SQL ,以此來完成動態
SQL 的功能。

  

5、最佳實踐中,通常一個 XML 映射文件,都會寫一個 Mapper 接口與之對應。請問,這個
Mapper 接口的工作原理是什么?Mapper 接口里的方法,參數不同時,方法能重載嗎?


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 對
象。
另外,Mapper 接口的實現類,通過 MyBatis 使口 JDK Proxy ⾃動⽣成其代理對象 Proxy ,而代理對象 Proxy 會攔截接口方法,從
而“調用”對應的 MappedStatement 方法,最終執行 SQL ,返回執⾏結果。整體流程如下圖:

 

 

其中,SqlSession 在調用 Executor 之前,會獲得對應的 MappedStatement 方法。例如:DefaultSqlSession#select(String statement,

Object parameter, RowBounds rowBounds, ResultHandler handler)方法,代碼如下:

 

1// DefaultSqlSession.java
2
3@Override
4publicvoidselect(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler){
5try{
6
// 獲得 MappedStatement 對象
7
MappedStatement ms = configuration.getMappedStatement(statement);
8
// 執⾏查詢
9
executor.query(ms,wrapCollection(parameter), rowBounds, handler);
10}catch(Exception e){
11
throw ExceptionFactory.wrapException("Error querying database. Cause: "+ e, e);
12}finally{
13
ErrorContext.instance().reset();
14}
15}

  

Mapper 接口里的方法,是不能重載的,因為是全限名 + 方法名的保存和尋找策略。所以有時,想個 Mapper 接口里的方法名,還
是蠻鬧心的,嘿嘿。


6、Mapper 接口綁定有幾種實現方式,分別是怎么實現的?


接口綁定有三種實現方式:
  第一種,通過 XML Mapper 里面寫 SQL 來綁定。在這種情況下,要指定 XML 映射文件里面的 "namespace" 必須為接⼝的全路徑名。

  第二種,通過注解綁定,就是在接口的方法上面加上 @Select、@Update、@Insert、@Delete 注解,里面包含 SQL 語句來綁定。
  第三種,是第二種的特例,也是通過注解綁定,在接口的方法上面加上


@SelectProvider、@UpdateProvider、@InsertProvider、@DeleteProvider注解,通過 Java 代碼,生成對應的動態 SQL 。
實際場景下,最最最推薦的是第一種方式。因為,SQL 通過注解寫在 Java 代碼中,會非常雜亂。而寫在 XML 中,更加
有整體性,並且可以更加方便的使用 OGNL 表達式。

7、Mybatis 的 XML Mapper文件中,不同的 XML 映射文件,id 是否可以重復?


不同的 XML Mapper 文件,如果配置了 "namespace",那么 id 可以重復;如果沒有配置 "namespace" ,那么 id 不能重復。畢
竟"namespace" 不是必須的,只是最佳實踐而已。
原因就是,namespace + id 是作為Map<String, MappedStatement> 的 key 使用的。如果沒有"namespace",就剩下 id ,那么 id
重復會導致數據互相覆蓋。如果有了"namespace",自然 id 就可以重復,"namespace"不同,namespace + id 自然也就不同。


8、如何獲取自動生成的(主)鍵值?


不同的數據庫,獲取自動生成的(主)鍵值的方式是不同的。MySQL 有兩種⽅式,但是自增主鍵,代碼如下:

1// 方式一,使用 useGeneratedKeys + keyProperty 屬性
2<insert id="insert" parameterType="Person" useGeneratedKeys="true" keyProperty="id">
3INSERTINTOperson(name, pswd)
4VALUE(#{name}, #{pswd})
5</insert>
6
7// 方式二,使用 `<selectKey />` 標簽
8<insert id="insert" parameterType="Person">
9<selectKey keyProperty="id" resultType="long" order="AFTER">
10
SELECTLAST_INSERT_ID()
11</selectKey>
12
13INSERTINTOperson(name, pswd)
14VALUE(#{name}, #{pswd})
15</insert>


其中,方式一較為常用。
Oracle 有兩種方式,序列和觸發器。因為自己不了解 Oracle ,所以問了銀行的朋友,他們是使用序列。而基於序列,根據
<selectKey /> 執行的時機,也有兩種方式,代碼如下:

// 這個是創建表的⾃增序列
CREATESEQUENCE student_sequence
INCREMENTBY1
NOMAXVALUE
NOCYCLE
CACHE10;
1
2// 方式一,使用 `<selectKey />` 標簽 + BEFORE
3<insert id="add" parameterType="Student">
4<selectKey keyProperty="student_id" resultType="int" order="BEFORE">
5 select student_sequence.nextval FROM dual
6</selectKey>
7
8INSERTINTOstudent(student_id, student_name, student_age)
9VALUES(#{student_id},#{student_name},#{student_age})
10</insert>
11
12// 方式二,使用 `<selectKey />` 標簽 + AFTER
13<insert id="save" parameterType="com.threeti.to.ZoneTO">
14<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
15SELECTSEQ_ZONE.CURRVALAS id FROM dual
16</selectKey>
17
18INSERTINTOTBL_ZONE(ID,NAME)
19VALUES(SEQ_ZONE.NEXTVAL, #{name,jdbcType=VARCHAR})
20</insert>

  

他們使用第一種方式,沒有具體原因,可能就沒什么講究吧。
至於為什么不用觸發器呢?朋友描述如下:

1朋友:觸發器不用啊,我們這邊原來也有觸發器,一有數據更改就會有問題了呀
2xx:數據更改指的是?
3朋友:就改線上某幾條數據
4xx:噢噢。手動改是吧?
5朋友:不行~

 

9、Mybatis 執⾏批量插⼊,能返回數據庫主鍵列表嗎?

mapper.xml層代碼

1<!--批量新增-->
2<insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id"
3
INSERTINTO
4
<include refid="t_shop_resource"/>
5
(relation_id, summary_id, relation_type)
6
VALUES
7
<foreach collection="list" index="index" item="shopResource" separator=",">
8
(
9
#{shopResource.relationId}, #{shopResource.summaryId}, #{shopResource.relationType}
10
)
11
</foreach>
12</insert>

dao實現層代碼

1public List<ShopResource>batchinsertCallId(List<ShopResource> shopResourceList){
2
this.getSqlSession().insert(getStatement(SQL_BATCH_INSERT_CALL_ID), shopResourceList);
3
return shopResourceList;// 重點介紹
4}

為什么最后返回的參數不是挑用mybatis后的insert的返回值呢,細心的話可以發現,如果使用debug模式觀察,會看
到調用mybatis后insert的返回值是[],也就是空集合元素.
在mybatis3.3.1中,雖然加入了批量新增返回主鍵id的功能,但是它是這樣運行的,在需要新增插⼊新元素集合對象
時,它會需要參數對象,當執行完插入操作后,給之前的參數對象設置id值,也就是改變了需要插入對象集合中的元
素的屬性id值,所以接收返回時,返回方法形參參數即可,同樣的地址引用改變了內容,返回后的集合也是改變后的集合。


10、在 Mapper 中如何傳遞多個參數?

第一種,使用 Map 集合,裝載多個參數進行傳遞。代碼如下:

1// 調用方法
2Map<String, Object> map =newHashMap();
3map.put("start", start);
4map.put("end", end);
5return studentMapper.selectStudents(map);
6
7// Mapper 接口
8List<Student>selectStudents(Map<String, Object> map);
9
10// Mapper XML 代碼
11<select id="selectStudents" parameterType="Map" resultType="Student">
12SELECT*
13FROM students 
14LIMIT #{start}, #{end}
15</select>

顯然,這不是一種優雅的方式。


第二種,保持傳遞多個參數,使用 @Param 注解。代碼如下:

1// 調用方法
2return studentMapper.selectStudents(0,10);
3
4// Mapper 接口
5List<Student>selectStudents(@Param("start") Integer start, @Param("end") Integer end);
6
7// Mapper XML 代碼
8<select id="selectStudents" resultType="Student">
9SELECT*
10FROM students 
11LIMIT #{start}, #{end}
12</select>

推薦使用這種方式。


第三種,保持傳遞多個參數,不使用 @Param 注解。代碼如下:

1// 調用方法
2return studentMapper.selectStudents(0,10);
3
4// Mapper 接口
5List<Student>selectStudents(Integer start, Integer end);
6
7// Mapper XML 代碼
8<select id="selectStudents" resultType="Student">
9SELECT*
10FROM students 
11LIMIT #{param1}, #{param2}
12</select>

其中,按照參數在⽅法⽅法中的位置,從 1 開始,逐個為#{param1}、#{param2}、#{param3} 不斷向下。


11、Mybatis 是否可以映射 Enum 枚舉類?


Mybatis 可以映射枚舉類,對應的實現類為 EnumTypeHandler 或 EnumOrdinalTypeHandler 。
  EnumTypeHandler ,基於 Enum.name 屬性( String )。默認。
  EnumOrdinalTypeHandler ,基於 Enum.ordinal 屬性( int )。可通過 <setting name="defaultEnumTypeHandler"
  value="EnumOrdinalTypeHandler" /> 來設置。


當然,實際開發場景,我們很少使⽤ Enum 類型,更加的⽅式是,代碼如下:

1publicclassDog{
2
3publicstatic final int STATUS_GOOD=1;
4publicstatic final int STATUS_BETTER=2;
5publicstatic final int STATUS_BEST=3;
6
7private int status;
8
9}

並且,不單可以映射枚舉類,Mybatis 可以映射任何對象到表的⼀列上。映射方式為自定義一個 TypeHandler 類,實現 TypeHandler
的#setParameter(...)和 #getResult(...)接⼝⽅法。


TypeHandler 有兩個作用:
一是,完成從 javaType 至 jdbcType 的轉換。
二是,完成 jdbcType 至 javaType 的轉換。


具體體現為#setParameter(...)和 #getResult(..)兩個方法,分別代表設置 SQL 問號占位符參數和獲取列查詢結果。
http://svip.iocoder.cn/MyBatis/type-package/


12、Mybatis 都有哪些 Executor 執⾏器?它們之間的區別是什么?


Mybatis 有四種 Executor 執行器,分別是 SimpleExecutor、ReuseExecutor、BatchExecutor、CachingExecutor 。
  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 批處理是相同。
  CachingExecutor :在上述的三個執行器之上,增加二級緩存的功能。
通過設置<setting name="defaultExecutorType" value=""> 的 "value" 屬性,可傳入 SIMPLE、REUSE、BATCH 三個值,分別使用
  SimpleExecutor、ReuseExecutor、BatchExecutor 執行器。
通過設置 <setting name="cacheEnabled" value=""的 "value"屬性為 true 時,創建 CachingExecutor執行器。


13、MyBatis 如何執行批量插入?


首先,在 Mapper XML 編寫一個簡單的 Insert 語句。代碼如下:

1<insert id="insertUser" parameterType="String">
2INSERTINTOusers(name)
3VALUES(#{value})
4</insert>

然后,然后在對應的 Mapper 接口中,編寫映射的方法。代碼如下:

1publicinterfaceUserMapper{
2
3voidinsertUser(@Param("name") String name);
4
5}

最后,調用該 Mapper 接口方法。代碼如下:

1privatestatic SqlSessionFactory sqlSessionFactory;
2
3@Test
4publicvoidtestBatch(){
5// 創建要插入的用戶的名字的數組
6 List<String> names =newArrayList<>();
7 names.add("王錘");
8 names.add("李凱爹");
9 names.add("徐媽");
10 names.add("哥");
11
12// 獲得執行器類型為 Batch 的 SqlSession 對象,並且 autoCommit = false ,禁止事務自動提交
13try(SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH,false)){
14
// 獲得 Mapper 對象
15
UserMapper mapper = session.getMapper(UserMapper.class);
16
// 循環插入
17
for(String name : names){
18
mapper.insertUser(name);
19
}
20
// 提交批量操作
21
session.commit();
22}
23}

代碼比較簡單,胖友仔細看看。當然,還有另一種方式,代碼如下:

1INSERTINTO[表名]([列名],[列名])
2VALUES
3([列值],[列值])),
4([列值],[列值])),
5([列值],[列值]));

對於這種方式,需要保證單條 SQL 不超過語句的最大限制max_allowed_packet大小,默認為 1 M。
這兩種方式的性能對比,可以看看https://www.jianshu.com/p/cce617be9f9e


14、介紹 MyBatis 的⼀級緩存和⼆級緩存的概念和實現原理?


1. 一級緩存介紹
在一次數據庫會話中,執行多次查詢條件完全相同的SQL,MyBatis提供了⼀級緩存的⽅案優化這部分場景,如果是相同
的SQL語句,會優先命中一級緩存,避免直接對數據庫進行查詢,提高性能。具體執行過程如下圖所示。


2. 一級緩存原理
每個SqlSession中持有了Executor,每個Executor中有一個LocalCache。當用戶發起查詢時,MyBatis根據當前執行的語
句⽣成MappedStatement,在Local Cache進行查詢,如果緩存命中的話,直接返回結果給用戶,如果緩存沒有命中的話,查
詢數據庫,結果寫入Local Cache,最后返回結果給用戶。具體實現類的類關系圖如下圖所示。

 

3. 二級緩存介紹
  二級緩存中,其最大的共享范圍就是一個SqlSession內部,如果多個SqlSession之間需要共享緩存,則需要使用到二級緩
存。開啟⼆級緩存后,會使用CachingExecutor裝飾Executor,進行二級緩存的查詢流程前,先在CachingExecutor進行二級
緩存的查詢,具體的操作流程如下所示。


二級緩存開啟后,同一個namespace下的所有操作語句,都影響着同一個Cache,即二級緩存被多個SqlSession共享,是
一個全局的變量。
當開啟緩存后,數據的查詢執行的流程就是二級緩存 -> 一級緩存 -> 數據庫。
4. 二級緩存原理
  MyBatis二級緩存的操作流程和前文提到的一級緩存類似,只是在一級緩存處理前,用CachingExecutor裝飾了
BaseExecutor的子類,在委托具體職責給delegate之前,實現了二級緩存的查詢和寫入功能。


15、Mybatis 是否支持延遲加載?如果支持,它的實現原理是什么?


Mybatis 僅支持 association 關聯對象和 collection 關聯集合對象的延遲加載。其中,association 指的就是一對一,collection 指的就
是一對多查詢。
在 Mybatis 配置文件中,可以配置 <setting name="lazyLoadingEnabled" value="true" /> 來啟⽤延遲加載的功能。默認情況下,延遲
加載的功能是關閉的。

它的原理是,使用 CGLIB 或 Javassist(默認)創建目標對象的代理對象。當調用代理對象的延遲加載屬性的 getting 方法時,進入攔
截器方法。比如調用a.getB().getName()方法,進入攔截器的 invoke(...)方法,發現 a.getB()需要延遲加載時,那么就會單獨發送事
先保存好的查詢關聯 B 對象的 SQL ,把 B 查詢上來,然后調用a.setB(b)方法,於是 a 對象 b 屬性就有值了,接着完
成a.getB().getName()方法的調用。這就是延遲加載的基本原理。
當然了,不光是 Mybatis,幾乎所有的包括 Hibernate 在內,支持延遲加載的原理都是一樣的

 

16、Mybatis 能否執行一對一、一對多的關聯查詢嗎?都有哪些實現方式,以及它們之間的區別。


  能,Mybatis 不僅可以執行一對一、一對多的關聯查詢,還可以執行多對一,多對多的關聯查詢。
關聯對象查詢,有兩種實現方式:
  一種是單獨發送一個 SQL 去查詢關聯對象,賦給主對象,然后返回主對象。好處是多條 SQL 分開,相對簡單,壞處是發起的
SQL 可能會比較多。
  另一種是使用嵌套查詢,嵌套查詢的含義為使用 join 查詢,一部分列是 A 對象的屬性值,另外一部分列是關聯對象 B 的屬性
值。好處是只發一個 SQL 查詢,就可以把主對象和其關聯對象查出來,壞處是 SQL 可能比較復雜。


那么問題來了,join 查詢出來 100 條記錄,如何確定主對象是 5 個,而不是 100 個呢?其去重復的原理是 <resultMap>標簽內的<id>
子標簽,指定了唯一確定一條記錄的 id 列。Mybatis 會根據<id> 列值來完成 100 條記錄的去重復功能,<id> 可以有多個,代表了聯
合主鍵的語意。


同樣主對象的關聯對象,也是根據這個原理去重復的。盡管一般情況下,只有主對象會有重復記錄,關聯對象一般不會重復。例如:
下面 join 查詢出來6條記錄,一、二列是 Teacher 對象列,第三列為 Student 對象列。Mybatis 去重復處理后,結果為 1 個老師和 6
個學生,而不是 6 個老師和 6 個學生。


17、簡述 Mybatis 的插件運用原理?以及如何編寫一個插件?

 

Mybatis 僅可以編寫針對 ParameterHandler、ResultSetHandler、StatementHandler、Executor 這 4 種接口的插件。
Mybatis 使用 JDK 的動態代理,為需要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這 4 種接口對象的方法時,就會
進入攔截方法,具體就是 InvocationHandler 的#invoke(...)方法。當然,只會攔截那些你指定需要攔截的方法。


編寫一個 MyBatis 插件的步驟如下:
  1. 首先,實現 Mybatis 的 Interceptor接口,並實現 #intercept(...)方法。
  2. 然后,在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可
  3. 最后,在配置文件中配置你編寫的插件。
插件的詳細解析http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins


18、Mybatis 是如何進行分頁的?分頁插件的原理是什么?


Mybatis 使用 RowBounds 對象進行分頁,它是針對 ResultSet 結果集執行的內存分頁,而非數據庫分頁。
所以,實際場景下,不適合直接使用 MyBatis 原有的 RowBounds 對象進行分頁。而是使用如下兩種方案:
  在 SQL 內直接書寫帶有數據庫分頁的參數來完成數據庫分頁功能
  也可以使用分頁插件來完成數據庫分頁。
這兩者都是基於數據庫分頁,差別在於前者是工程師手動編寫分頁條件,后者是插件自動添加分頁條件。
分頁插件的基本原理是使用 Mybatis 提供的插件接口,實現自定義分頁插件。在插件的攔截方法內,攔截待執行的 SQL ,然后重寫
SQL ,根據dialect 方言,添加對應的物理分頁語句和物理分頁參數。
舉例:SELECT * FROM student ,攔截 SQL 后重寫為:select * FROM student LIMI 0,10 。
目前市面上目前使用比較廣泛的 MyBatis 分頁插件有:
  Mybatis-PageHelper
  MyBatis-Plus
從現在看來,MyBatis-Plus 逐步使用的更加廣泛。

19、MyBatis 與 Hibernate 有哪些不同?

Mybatis 和 Hibernate 不同,它不完全是一個 ORM 框架,因為MyBatis 需要程序員自己編寫 SQL 語句。不過 MyBatis 可以通過
XML 或注解方式靈活配置要運行的 SQL 語句,並將 Java 對象和 SQL 語句映射生成最終執行的 SQL ,最后將 SQL 執行的結果再映射
生成 Java 對象。
Mybatis 學習門檻低,簡單易學,程序員直接編寫原生態 SQL ,可嚴格控制 SQL 執行性能,靈活度高。但是靈活的前提是
MyBatis 方法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套 SQL 映射文件,工作量大。
Hibernate 對象/關系映射能力強,數據庫無關性好。如果用 Hibernate 開發可以節省很多代碼,提高效率。但是 Hibernate 的缺
點是學習門檻低,要精通門檻更高,而且怎么設計 O/R 映射,在性能和對象模型之間如何權衡,以及怎樣用好 Hibernate 需要具有很
強的經驗和能力才行。


總之,按照用戶的需求在有限的資源環境下只要能做出維護性、擴展性良好的軟件架構都是好架構,所以框架只有適合才是最好。簡
單總結如下:


  Hibernate 屬於全自動 ORM 映射工具,使用 Hibernate 查詢關聯對象或者關聯集合對象時,可以根據對象關系模型直接獲取。
  Mybatis 屬於半自動 ORM 映射工具,在查詢關聯對象或關聯集合對象時,需要手動編寫 SQL 來完成。
另外,在https://www.jianshu.com/p/96171e647885文章,也是寫的非常不錯的。
當然,實際上,MyBatis 也可以搭配自動生成代碼的工具,提升開發效率,還可以使用 MyBatis-Plus 框架,已經內置常用的 SQL 操
作,也是非常不錯的。


20、JDBC 編程有哪些不足之處,MyBatis是如何解決這些問題的?


問題一:SQL 語句寫在代碼中造成代碼不易維護,且代碼會比較混亂。
  解決方式:將 SQL 語句配置在 Mapper XML 文件中,與 Java 代碼分離。
問題二:根據參數不同,拼接不同的 SQL 語句非常麻煩。例如 SQL 語句的 WHERE 條件不一定,可能多也可能少,占位符需要和參
數一一對應。
  解決方式:MyBatis 提供 <where />、<if /> 等等動態語句所需要的標簽,並支持 OGNL 表達式,簡化了動態 SQL 拼接的代碼,提升
了開發效率。
問題三,對結果集解析麻煩,SQL 變化可能導致解析代碼變化,且解析前需要遍歷。
  解決⽅式:Mybatis 自動將 SQL 執行結果映射成 Java 對象。
問題四,數據庫鏈接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫鏈接池可解決此問題。
  解決⽅式:在 mybatis-config.xml 中,配置數據鏈接池,使用連接池管理數據庫鏈接。
當然,即使不使用MyBatis ,也可以使用數據庫連接池。
另外,MyBatis 默認提供了數據庫連接池的實現,只是說,因為其它開源的數據庫連接池性能更好,所以一般很少使用 MyBatis 自帶
的連接池實現。


21、Mybatis 比 IBatis 比較大的幾個改進是什么?


1. 有接口綁定,包括注解綁定 SQL 和 XML 綁定 SQL 。
2. 動態 SQL 由原來的節點配置變成 OGNL 表達式。
3. 在一對一或一對多的時候,引進了 association ,在一對多的時候,引用了 collection節點,不過都是在 <resultMap /> 里面配
置。


22、Mybatis 映射文件中,如果 A 標簽通過 include 引用了B標簽的內容,請問,B 標簽能否定義在 A 標簽的后面,還是說必須定義在A標簽的前面?


雖然 Mybatis 解析 XML 映射文件是按照順序解析的。但是,被引用的 B 標簽依然可以定義在任何地方,Mybatis 都可以正確識別。也
就是說,無需按照順序,進行定義。
原理是,Mybatis 解析 A 標簽,發現 A 標簽引用了 B 標簽,但是 B 標簽尚未解析到,尚不存在,此時,Mybatis 會將 A 標簽標記為未
解析狀態。然后,繼續解析余下的標簽,包含 B 標簽,待所有標簽解析完畢,Mybatis 會重新解析那些被標記為未解析的標簽,此時
再解析A標簽時,B 標簽已經存在,A 標簽也就可以正常解析完成了。


23、簡述 Mybatis 的 XML 映射⽂件和 Mybatis 內部數據結構之間的映射關系?

Mybatis 將所有 XML 配置信息都封裝到 All-In-One 重量級對象Configuration內部。
在 XML Mapper 文件中:
<parameterMap> 標簽,會被解析為 ParameterMap 對象,其每個子元素會被解析為 ParameterMapping 對象。
<resultMap> 標簽,會被解析為 ResultMap 對象,其每個子元素會被解析為 ResultMapping 對象。
每⼀個 <select>、<insert>、<update>、<delete> 標簽,均會被解析為一個 MappedStatement 對象,標簽內的 SQL 會被解
析為⼀個 BoundSql 對象。


24、MyBatis的優缺點


優點:
  1. 小巧學習成本低,會寫sql上手很快
  2. 比jdbc,基本配置好了,大部分的工作量就專注在sql的部分
  3. 方便維護管理,sql不需要再Java代碼中找,sql代碼可以分離出來,重用
  4. 接近jdbc,靈活,支持動態sql
  5. 支持對象與數據庫的orm映射
缺點:
  1.由於工作量在sql上,需要sql的熟練度高
  2.移植性差,sql語法依賴數據庫,不同過的數據庫切換會因語法差異,會報錯。


25、MyBatis的Mapper接口中方法能不能重載?為什么?


答案:不能,由於MyBatis在獲取Mapper接口方法的代理時,是通過package+Mapper+method全限定名作為key,去xml內尋找唯一sql
來執行的。
類似:key=com.cc.mapper.UserMapper.listByPage,那么重載方法時將導致矛盾。對於Mapper接口,MyBatis禁用方法重載
(OverLoad)


26、MyBatis的自定義一個typeHandler類和設置parameterType,resultType有什么區別嗎?


分別適合在什么場景下使用?
答案:不能,由於MyBatis在獲取Mapper接口方法的代理時,是通過package+Mapper+method全限定名作為key,去xml內尋找唯一sql
來執行的。
類似:key=com.cc.mapper.UserMapper.listByPage,那么重載方法時將導致⽭盾。對於Mapper接口,MyBatis禁用方法重載
(OverLoad)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM