一、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 查詢粪薛,存儲(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)中的屬性名和表中的字段名不一致录淡?
八、 模糊查詢like語(yǔ)句該怎么寫(xiě)?
九油坝、Mybatis 的緩存分幾種嫉戚?各有什么作用刨裆?
MyBatis 中的緩存分為兩種:一級(jí)緩存和二級(jí)緩存。一級(jí)緩存是 sqlSession 級(jí)別的彬檀,二級(jí)緩存是 mapper 級(jí)別的帆啃。當(dāng)使用同一個(gè) sqlSession 時(shí),查詢到的數(shù)據(jù)可能是一級(jí)緩存窍帝;而當(dāng)使用同一個(gè) mapper 是努潘,查詢到的數(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查詢關(guān)聯(lián)對(duì)象或者關(guān)聯(lián)集合對(duì)象時(shí)钧汹,可以根據(jù)對(duì)象關(guān)系模型直接獲取丈探,所以它是全自動(dòng)的。而Mybatis在查詢關(guān)聯(lián)對(duì)象或關(guān)聯(lián)集合對(duì)象時(shí)拔莱,需要手動(dòng)編寫(xiě)sql來(lái)完成碗降,所以,稱之為半自動(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ù)編譯階段可以優(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ù)化查詢。使用預(yù)處理語(yǔ)句比普通的查詢更快臣淤,因?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)在第一次查詢前完成了逮京。 - PreparedStatement可以防止SQL注入式攻擊
SQL 注入攻擊:SQL 注入是利用某些系統(tǒng)沒(méi)有對(duì)用戶輸入的數(shù)據(jù)進(jìn)行充分的檢查,而在用戶輸入數(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)就在此妻导。