Mybatis 相關(guān)

一、Ibatis和Mybatis

  1. Ibatis:2010年,apache 的 Ibatis 框架停止更新,并移交給了 google 團(tuán)隊(duì),同時(shí)更名為 MyBatis。從2010年后 Ibatis 再?zèng)]更新過(guò),徹底變成了一個(gè)孤兒框架。一個(gè)沒(méi)人維護(hù)的框架注定被 MyBatis 拍在沙灘上。
  2. Mybatis:Ibatis 的升級(jí)版本。

二、Mybatis DAO接口為什么不需要實(shí)現(xiàn)類(lèi)?

Mybatis 實(shí)現(xiàn)了 DAO 接口 與 xml 映射文件的綁定,自動(dòng)生成接口的具體實(shí)現(xiàn),使用起來(lái)變得更加省事和方便。

1??Dao 接口,就是常說(shuō)的 Mapper 接口:
①接口的全限名,就是映射文件中的 namespace 的值。
②接口的方法名,就是映射文件中 MappedStatement 的 id 值。
③接口方法內(nèi)的參數(shù),就是傳遞給 sql 的參數(shù)。

2??Dao 接口是沒(méi)有實(shí)現(xiàn)類(lèi)的,當(dāng)調(diào)用接口方法時(shí),接口全限名+方法名拼接字符串作為 key 值,可唯一定位一個(gè) MappedStatement。舉例:
com.wg.mappers.ZooDao.selById,可以唯一找到 namespace 為com.wg.mappers.ZooDao 下面 id = selById 的 MappedStatement。在 Mybatis 中,每一個(gè) select、insert、update、delete 標(biāo)簽,都會(huì)被解析為一個(gè) MappedStatement 對(duì)象。

3??Dao 接口里的方法,是不能重載的,因?yàn)槭侨廾?方法名的保存和尋找策略。Dao 接口的工作原理是 JDK 的動(dòng)態(tài)代理,Mybatis 運(yùn)行時(shí)會(huì)使用 JDK 動(dòng)態(tài)代理為 Dao 接口生成代理 proxy 對(duì)象(如使用 Spring 會(huì)注入到容器中),代理對(duì)象 proxy 會(huì)攔截接口方法,轉(zhuǎn)而執(zhí)行 MappedStatement 所代表的 sql,然后將 sql 執(zhí)行結(jié)果返回。

三、什么情況用注解,什么情況用xml綁定?

  1. 注解使用情況:Sql語(yǔ)句簡(jiǎn)單時(shí)
  2. xml綁定使用情況:xml綁定 (@RequestMap用來(lái)綁定xml文件)

四、Mybatis核心處理類(lèi)叫什么?

SqlSession

五、Mybatis是用來(lái)做什么的?為什么不直接用jdbc?

MyBatis 是一個(gè)支持普通 SQL 查詢(xún),存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架。
相對(duì)于 JDBC,MyBatis 有以下優(yōu)點(diǎn):
①把 sql 語(yǔ)句從 Java 中獨(dú)立出來(lái),統(tǒng)一管理,便于維護(hù)和管理。
②封裝了底層的 JDBC,API 的調(diào)用,并且能夠?qū)⒔Y(jié)果集自動(dòng)轉(zhuǎn)換成 JavaBean 對(duì)象,簡(jiǎn)化了 Java 數(shù)據(jù)庫(kù)編程的重復(fù)工作。
③提供了緩存功能。
④入?yún)o(wú)需用對(duì)象封裝(或者map封裝),使用 @Param 注解。

六、#{}和${}的區(qū)別

區(qū)別
理解

七、怎么處理實(shí)體類(lèi)中的屬性名和表中的字段名不一致?

名稱(chēng)對(duì)應(yīng)

八、 模糊查詢(xún)like語(yǔ)句該怎么寫(xiě)?

模糊查詢(xún)like語(yǔ)句

九、Mybatis 的緩存分幾種?各有什么作用?

MyBatis 中的緩存分為兩種:一級(jí)緩存和二級(jí)緩存。一級(jí)緩存是 sqlSession 級(jí)別的,二級(jí)緩存是 mapper 級(jí)別的。當(dāng)使用同一個(gè) sqlSession 時(shí),查詢(xún)到的數(shù)據(jù)可能是一級(jí)緩存;而當(dāng)使用同一個(gè) mapper 是,查詢(xún)到的數(shù)據(jù)可能是二級(jí)緩存。一級(jí)緩存默認(rèn)開(kāi)啟的。二級(jí)緩存需要配置才開(kāi)啟,當(dāng)配置文件配置了cacheEnabled=true時(shí),就會(huì)開(kāi)啟二級(jí)緩存。

十、Mybatis是如何進(jìn)行分頁(yè)的?分頁(yè)插件的原理是什么?

  1. Mybatis 使用 RowBounds 對(duì)象進(jìn)行分頁(yè),它是針對(duì) ResultSet 結(jié)果集執(zhí)行的內(nèi)存分頁(yè),而非物理分頁(yè)。
  2. 也可以在 sql 內(nèi)直接書(shū)寫(xiě)帶有物理分頁(yè)的參數(shù)來(lái)完成物理分頁(yè)功能,也可以使用分頁(yè)插件來(lái)完成物理分頁(yè)。分頁(yè)插件的基本原理是使用 Mybatis 提供的插件接口,實(shí)現(xiàn)自定義插件,在插件的攔截方法內(nèi)攔截待執(zhí)行的 sql,然后重寫(xiě) sql,根據(jù) dialect 方言,添加對(duì)應(yīng)的物理分頁(yè)語(yǔ)句和物理分頁(yè)參數(shù)。

十一、Mybatis是如何將sql執(zhí)行結(jié)果封裝為目標(biāo)對(duì)象并返回的?都有哪些映射形式?

1.使用resultMap標(biāo)簽,逐一定義列名和對(duì)象屬性名之間的映射關(guān)系。
2.使用sql列的別名功能,將列別名書(shū)寫(xiě)為對(duì)象屬性名,比如AcctName AS NAME,對(duì)象屬性名一般是小寫(xiě),但是列名不區(qū)分大小寫(xiě),Mybatis會(huì)忽略列名大小寫(xiě),智能找到與之對(duì)應(yīng)對(duì)象屬性名,你甚至可以寫(xiě)成AcctName AS NaMe,Mybatis一樣可以正常工作。
有了列名與屬性名的映射關(guān)系后,Mybatis通過(guò)反射創(chuàng)建對(duì)象,同時(shí)使用反射給對(duì)象的屬性逐一賦值并返回,那些找不到映射關(guān)系的屬性,是無(wú)法完成賦值的。

十二、如何執(zhí)行批量插入

首先,創(chuàng)建一個(gè)簡(jiǎn)單的insert語(yǔ)句:

<insert id=”insertName”> 
 insert into acctInfo (name) values (#{value}) 
</insert>

然后在java代碼中像下面這樣執(zhí)行批處理插入:

List<String> names = new ArrayList(); 
names.add("fred"); 
names.add("barney"); 
names.add("betty"); 
names.add("wilma"); 
// 注意這里 executortype.batch 
sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch); 
try { 
    Namemapper mapper = sqlsession.getmapper(namemapper.class); 
    for (String name : names) { 
      mapper.insertName(name); 
     } 
    sqlsession.commit(); 
  } finally { 
    sqlsession.close(); 
  }

十三、如何獲取自動(dòng)生成的(主)鍵值?

insert 方法總是返回一個(gè)int值 ~~ 這個(gè)值代表的是插入的行數(shù)。
而自動(dòng)生成的鍵值在 insert 方法執(zhí)行完后可以被設(shè)置到傳入的參數(shù)對(duì)象中。
示例:

 <insert id="insertName" usegeneratedkeys="true" keyproperty="id"> 
     insert into names (name) values (#{name}) 
 </insert>
 Name name = new Name(); 
 name.setName(“fred”); 
 int rows = mapper.insertName(name); 
 // 完成后,id已經(jīng)被設(shè)置到對(duì)象中 
 system.out.println("rows inserted = " + rows); 
 system.out.println("generated key value = " + name.getId());

十四、在mapper中如何傳遞多個(gè)參數(shù)?

第1種:

//DAO層的函數(shù)
public User selUser(String name,String area); 
//對(duì)應(yīng)的xml,#{0}代表接收的是dao層中的第一個(gè)參數(shù),#{1}代表dao層中第二參數(shù),
更多參數(shù)一致往后加即可。
<select id="selectUser" resultMap="BaseResultMap"> 
 select * from userInfo where user_name = #{0} and user_area=#{1} 
</select> 

第2種: 使用 @param 注解:

 public interface usermapper { 
 user selUser(@param("userName") String userName, 
 @param("hashedPassword") String hashedPassword); 
 }
然后,就可以在xml像下面這樣使用(推薦封裝為一個(gè)map,作為單個(gè)參數(shù)傳遞給mapper): 
 <select id="selUser" resultType="user"> 
 select id, username, hashedPassword 
 from userInfo 
 where userName = #{username} 
 and hashedPassword = #{hashedPassword} 
 </select>

十五、Mybatis動(dòng)態(tài)sql是做什么的?都有哪些動(dòng)態(tài)sql?能簡(jiǎn)述一下動(dòng)態(tài)sql的執(zhí)行原理不?

Mybatis動(dòng)態(tài)sql可以讓我們?cè)赬ml映射文件內(nèi),以標(biāo)簽的形式編寫(xiě)動(dòng)態(tài)sql,完成邏輯判斷和動(dòng)態(tài)拼接sql的功能。
Mybatis提供了9種動(dòng)態(tài)sql標(biāo)簽:trim|where|set|foreach|if|choose|when|otherwise|bind。
其執(zhí)行原理為,使用OGNL從sql參數(shù)對(duì)象中計(jì)算表達(dá)式的值,根據(jù)表達(dá)式的值動(dòng)態(tài)拼接sql,以此來(lái)完成動(dòng)態(tài)sql的功能。

十六、Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重復(fù)?

不同的Xml映射文件,如果配置了namespace,那么id可以重復(fù);如果沒(méi)有配置namespace,那么id不能重復(fù);畢竟namespace不是必須的,只是最佳實(shí)踐而已。
原因就是namespace+id是作為Map<String, MappedStatement>的key使用的,如果沒(méi)有namespace,就剩下id。那么,id重復(fù)會(huì)導(dǎo)致數(shù)據(jù)互相覆蓋。有了namespace,自然id就可以重復(fù),namespace不同,namespace+id自然也就不同。

十七、為什么說(shuō)Mybatis是半自動(dòng)ORM映射工具?它與全自動(dòng)的區(qū)別在哪里?

Hibernate屬于全自動(dòng)ORM映射工具,使用Hibernate查詢(xún)關(guān)聯(lián)對(duì)象或者關(guān)聯(lián)集合對(duì)象時(shí),可以根據(jù)對(duì)象關(guān)系模型直接獲取,所以它是全自動(dòng)的。而Mybatis在查詢(xún)關(guān)聯(lián)對(duì)象或關(guān)聯(lián)集合對(duì)象時(shí),需要手動(dòng)編寫(xiě)sql來(lái)完成,所以,稱(chēng)之為半自動(dòng)ORM映射工具。

十八、sql預(yù)編譯

sql 預(yù)編譯指的是數(shù)據(jù)庫(kù)驅(qū)動(dòng)在發(fā)送 sql 語(yǔ)句和參數(shù)給 DBMS 數(shù)據(jù)庫(kù)管理系統(tǒng)(Database Management System)之前對(duì) sql 語(yǔ)句進(jìn)行編譯,這樣 DBMS 執(zhí)行 sql 時(shí),就不需要重新編譯。
為什么需要預(yù)編譯?
JDBC 中使用對(duì)象 PreparedStatement 來(lái)抽象預(yù)編譯語(yǔ)句,使用預(yù)編譯。

  1. 預(yù)編譯階段可以?xún)?yōu)化 sql 的執(zhí)行。
    預(yù)編譯之后的 sql 多數(shù)情況下可以直接執(zhí)行,DBMS不需要再次編譯,越復(fù)雜的sql,編譯的復(fù)雜度將越大,預(yù)編譯階段可以合并多次操作為一個(gè)操作。
  2. 預(yù)編譯語(yǔ)句對(duì)象可以重復(fù)利用。
    SQL語(yǔ)句會(huì)預(yù)編譯在數(shù)據(jù)庫(kù)系統(tǒng)中。執(zhí)行計(jì)劃同樣會(huì)被緩存起來(lái),它允許數(shù)據(jù)庫(kù)做參數(shù)化查詢(xún)。使用預(yù)處理語(yǔ)句比普通的查詢(xún)更快,因?yàn)樗龅墓ぷ鞲佟0岩粋€(gè) sql 預(yù)編譯后產(chǎn)生的 PreparedStatement 對(duì)象緩存下來(lái),下次對(duì)于同一個(gè)sql,可以直接使用這個(gè)緩存的PreparedState 對(duì)象。它擁有更佳的性能優(yōu)勢(shì),因?yàn)閿?shù)據(jù)庫(kù)對(duì)SQL語(yǔ)句的分析,編譯,優(yōu)化已經(jīng)在第一次查詢(xún)前完成了。
  3. PreparedStatement可以防止SQL注入式攻擊
    SQL 注入攻擊:SQL 注入是利用某些系統(tǒng)沒(méi)有對(duì)用戶(hù)輸入的數(shù)據(jù)進(jìn)行充分的檢查,而在用戶(hù)輸入數(shù)據(jù)中注入非法的 SQL 語(yǔ)句段或命令,從而利用系統(tǒng)的 SQL 引擎完成惡意行為的做法。

MyBatis 默認(rèn)情況下,將對(duì)所有的 sql 進(jìn)行預(yù)編譯。

MySQL預(yù)編譯源碼解析:
MySQL的預(yù)編譯源碼在 com.mysql.jdbc.ConnectionImpl 類(lèi)中,如下:


說(shuō)明:
MyBatis在調(diào)用 connection 進(jìn)行 sql 預(yù)編譯之前,會(huì)對(duì)sql語(yǔ)句進(jìn)行動(dòng)態(tài)解析,動(dòng)態(tài)解析主要包含如下的功能:

  • 占位符的處理
  • 動(dòng)態(tài)sql的處理
  • 參數(shù)類(lèi)型校驗(yàn)
    MyBatis強(qiáng)大的動(dòng)態(tài)SQL功能的具體實(shí)現(xiàn)就在此。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容