一、Ibatis和Mybatis
- Ibatis:2010年,apache 的 Ibatis 框架停止更新,并移交給了 google 團(tuán)隊(duì),同時(shí)更名為 MyBatis。從2010年后 Ibatis 再?zèng)]更新過(guò),徹底變成了一個(gè)孤兒框架。一個(gè)沒(méi)人維護(hù)的框架注定被 MyBatis 拍在沙灘上。
- 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綁定?
- 注解使用情況:Sql語(yǔ)句簡(jiǎn)單時(shí)
- 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ū)別


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

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

九、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è)插件的原理是什么?
- Mybatis 使用 RowBounds 對(duì)象進(jìn)行分頁(yè),它是針對(duì) ResultSet 結(jié)果集執(zhí)行的內(nèi)存分頁(yè),而非物理分頁(yè)。
- 也可以在 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ù)編譯。
- 預(yù)編譯階段可以?xún)?yōu)化 sql 的執(zhí)行。
預(yù)編譯之后的 sql 多數(shù)情況下可以直接執(zhí)行,DBMS不需要再次編譯,越復(fù)雜的sql,編譯的復(fù)雜度將越大,預(yù)編譯階段可以合并多次操作為一個(gè)操作。 - 預(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)前完成了。 - 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)就在此。