1.#{}和${}的區(qū)別是什么刻恭?
答:{driver}會被靜態(tài)替換為com.mysql.jdbc.Driver惑折。#{}是sql的參數(shù)占位符涉馅,Mybatis會將sql中的#{}替換為?號归园,在sql執(zhí)行前會使用PreparedStatement的參數(shù)設(shè)置方法,按序給sql的?號占位符設(shè)置參數(shù)值稚矿,比如ps.setInt(0, parameterValue)庸诱,#{item.name}的取值方式為使用反射從參數(shù)對象中獲取item對象的name屬性值,相當(dāng)于param.getItem().getName()盐捷。
2. Xml映射文件中偶翅,除了常見的select|insert|updae|delete標(biāo)簽之外,還有哪些標(biāo)簽碉渡?
答:還有很多其他的標(biāo)簽聚谁,<resultMap>、<parameterMap>滞诺、<sql>形导、<include>、<selectKey>习霹,加上動態(tài)sql的9個標(biāo)簽朵耕,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中<sql>為sql片段標(biāo)簽淋叶,通過<include>標(biāo)簽引入sql片段阎曹,<selectKey>為不支持自增的主鍵生成策略標(biāo)簽。
3.最佳實踐中煞檩,通常一個Xml映射文件处嫌,都會寫一個Dao接口與之對應(yīng),請問斟湃,這個Dao接口的工作原理是什么熏迹?Dao接口里的方法,參數(shù)不同時凝赛,方法能重載嗎注暗?
答:Dao接口坛缕,就是人們常說的Mapper接口,接口的全限名捆昏,就是映射文件中的namespace的值赚楚,接口的方法名,就是映射文件中MappedStatement的id值屡立,接口方法內(nèi)的參數(shù)直晨,就是傳遞給sql的參數(shù)。
Mapper接口是沒有實現(xiàn)類的膨俐,當(dāng)調(diào)用接口方法時勇皇,接口全限名+方法名拼接字符串作為key值,可唯一定位一個MappedStatement焚刺,舉例:com.mybatis3.mappers.StudentDao.findStudentById敛摘,可以唯一找到namespace為com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中乳愉,每一個<select>兄淫、<insert>、<update>蔓姚、<delete>標(biāo)簽捕虽,都會被解析為一個MappedStatement對象。
Dao接口里的方法坡脐,是不能重載的泄私,因為是全限名+方法名的保存和尋找策略。
Dao接口的工作原理是JDK動態(tài)代理备闲,Mybatis運行時會使用JDK動態(tài)代理為Dao接口生成代理proxy對象晌端,代理對象proxy會攔截接口方法,轉(zhuǎn)而執(zhí)行MappedStatement所代表的sql恬砂,然后將sql執(zhí)行結(jié)果返回咧纠。
4.Mybatis是如何進行分頁的?分頁插件的原理是什么泻骤?
答:Mybatis使用RowBounds對象進行分頁漆羔,它是針對ResultSet結(jié)果集執(zhí)行的內(nèi)存分頁,而非物理分頁狱掂,可以在sql內(nèi)直接書寫帶有物理分頁的參數(shù)來完成物理分頁功能演痒,也可以使用分頁插件來完成物理分頁。
分頁插件的基本原理是使用Mybatis提供的插件接口符欠,實現(xiàn)自定義插件嫡霞,在插件的攔截方法內(nèi)攔截待執(zhí)行的sql瓶埋,然后重寫sql希柿,根據(jù)dialect方言诊沪,添加對應(yīng)的物理分頁語句和物理分頁參數(shù)。
舉例:select * from student曾撤,攔截sql后重寫為:select t.* from (select * from student)t limit 0端姚,10
5.簡述Mybatis的插件運行原理,以及如何編寫一個插件挤悉。
答:Mybatis僅可以編寫針對ParameterHandler渐裸、ResultSetHandler、StatementHandler装悲、Executor這4種接口的插件昏鹃,Mybatis使用JDK的動態(tài)代理,為需要攔截的接口生成代理對象以實現(xiàn)接口方法攔截功能诀诊,每當(dāng)執(zhí)行這4種接口對象的方法時洞渤,就會進入攔截方法,具體就是InvocationHandler的invoke()方法属瓣,當(dāng)然载迄,只會攔截那些你指定需要攔截的方法。
實現(xiàn)Mybatis的Interceptor接口并復(fù)寫intercept()方法抡蛙,然后在給插件編寫注解护昧,指定要攔截哪一個接口的哪些方法即可,記住粗截,別忘了在配置文件中配置你編寫的插件惋耙。
6.Mybatis執(zhí)行批量插入,能返回數(shù)據(jù)庫主鍵列表嗎慈格?
答:能怠晴,JDBC都能,Mybatis當(dāng)然也能浴捆。
7.Mybatis動態(tài)sql是做什么的蒜田?都有哪些動態(tài)sql?能簡述一下動態(tài)sql的執(zhí)行原理不选泻?
答:Mybatis動態(tài)sql可以讓我們在Xml映射文件內(nèi)冲粤,以標(biāo)簽的形式編寫動態(tài)sql,完成邏輯判斷和動態(tài)拼接sql的功能页眯,Mybatis提供了9種動態(tài)sql標(biāo)簽trim|where|set|foreach|if|choose|when|otherwise|bind梯捕。
其執(zhí)行原理為,使用OGNL從sql參數(shù)對象中計算表達式的值窝撵,根據(jù)表達式的值動態(tài)拼接sql傀顾,以此來完成動態(tài)sql的功能。
8.Mybatis是如何將sql執(zhí)行結(jié)果封裝為目標(biāo)對象并返回的碌奉?都有哪些映射形式短曾?
答:第一種是使用<resultMap>標(biāo)簽寒砖,逐一定義列名和對象屬性名之間的映射關(guān)系。第二種是使用sql列的別名功能嫉拐,將列別名書寫為對象屬性名哩都,比如T_NAME AS NAME,對象屬性名一般是name婉徘,小寫漠嵌,但是列名不區(qū)分大小寫,Mybatis會忽略列名大小寫盖呼,智能找到與之對應(yīng)對象屬性名儒鹿,你甚至可以寫成T_NAME AS NaMe,Mybatis一樣可以正常工作几晤。
有了列名與屬性名的映射關(guān)系后挺身,Mybatis通過反射創(chuàng)建對象,同時使用反射給對象的屬性逐一賦值并返回锌仅,那些找不到映射關(guān)系的屬性章钾,是無法完成賦值的。
9.Mybatis能執(zhí)行一對一热芹、一對多的關(guān)聯(lián)查詢嗎贱傀?都有哪些實現(xiàn)方式,以及它們之間的區(qū)別伊脓。
答:能府寒,Mybatis不僅可以執(zhí)行一對一、一對多的關(guān)聯(lián)查詢报腔,還可以執(zhí)行多對一株搔,多對多的關(guān)聯(lián)查詢,多對一查詢纯蛾,其實就是一對一查詢纤房,只需要把selectOne()修改為selectList()即可;多對多查詢翻诉,其實就是一對多查詢炮姨,只需要把selectOne()修改為selectList()即可。
關(guān)聯(lián)對象查詢碰煌,有兩種實現(xiàn)方式舒岸,一種是單獨發(fā)送一個sql去查詢關(guān)聯(lián)對象,賦給主對象芦圾,然后返回主對象蛾派。另一種是使用嵌套查詢,嵌套查詢的含義為使用join查詢,一部分列是A對象的屬性值洪乍,另外一部分列是關(guān)聯(lián)對象B的屬性值梭依,好處是只發(fā)一個sql查詢,就可以把主對象和其關(guān)聯(lián)對象查出來典尾。
那么問題來了,join查詢出來100條記錄糊探,如何確定主對象是5個钾埂,而不是100個?其去重復(fù)的原理是<resultMap>標(biāo)簽內(nèi)的<id>子標(biāo)簽科平,指定了唯一確定一條記錄的id列褥紫,Mybatis根據(jù)<id>列值來完成100條記錄的去重復(fù)功能,<id>可以有多個瞪慧,代表了聯(lián)合主鍵的語意髓考。
同樣主對象的關(guān)聯(lián)對象,也是根據(jù)這個原理去重復(fù)的弃酌,盡管一般情況下氨菇,只有主對象會有重復(fù)記錄,關(guān)聯(lián)對象一般不會重復(fù)妓湘。
舉例:下面join查詢出來6條記錄查蓉,一、二列是Teacher對象列榜贴,第三列為Student對象列豌研,Mybatis去重復(fù)處理后,結(jié)果為1個老師6個學(xué)生唬党,而不是6個老師6個學(xué)生鹃共。
t_id t_name s_id
1 teacher 38
1 teacher 39
1 teacher 40
1 teacher 41
1 teacher 42
1 teacher 43
10.Mybatis是否支持延遲加載?如果支持驶拱,它的實現(xiàn)原理是什么霜浴?
答:Mybatis僅支持association關(guān)聯(lián)對象和collection關(guān)聯(lián)集合對象的延遲加載,association指的就是一對一蓝纲,collection指的就是一對多查詢坷随。在Mybatis配置文件中,可以配置是否啟用延遲加載lazyLoadingEnabled=true|false驻龟。
它的原理是温眉,使用CGLIB創(chuàng)建目標(biāo)對象的代理對象,當(dāng)調(diào)用目標(biāo)方法時翁狐,進入攔截器方法类溢,比如調(diào)用a.getB().getName(),攔截器invoke()方法發(fā)現(xiàn)a.getB()是null值,那么就會單獨發(fā)送事先保存好的查詢關(guān)聯(lián)B對象的sql闯冷,把B查詢上來砂心,然后調(diào)用a.setB(b),于是a的對象b屬性就有值了蛇耀,接著完成a.getB().getName()方法的調(diào)用辩诞。這就是延遲加載的基本原理。
當(dāng)然了纺涤,不光是Mybatis译暂,幾乎所有的包括Hibernate,支持延遲加載的原理都是一樣的撩炊。
11.Mybatis的Xml映射文件中外永,不同的Xml映射文件,id是否可以重復(fù)拧咳?
答:不同的Xml映射文件伯顶,如果配置了namespace,那么id可以重復(fù)骆膝;如果沒有配置namespace祭衩,那么id不能重復(fù);畢竟namespace不是必須的阅签,只是最佳實踐而已汪厨。
原因就是namespace+id是作為Map<String, MappedStatement>的key使用的,如果沒有namespace愉择,就剩下id劫乱,那么,id重復(fù)會導(dǎo)致數(shù)據(jù)互相覆蓋锥涕。有了namespace衷戈,自然id就可以重復(fù),namespace不同层坠,namespace+id自然也就不同殖妇。
12.Mybatis中如何執(zhí)行批處理?
答:使用BatchExecutor完成批處理破花。
13.Mybatis都有哪些Executor執(zhí)行器谦趣?它們之間的區(qū)別是什么?
答:Mybatis有三種基本的Executor執(zhí)行器座每,SimpleExecutor前鹅、ReuseExecutor、BatchExecutor峭梳。
SimpleExecutor:每執(zhí)行一次update或select舰绘,就開啟一個Statement對象,用完立刻關(guān)閉Statement對象。
ReuseExecutor:執(zhí)行update或select捂寿,以sql作為key查找Statement對象口四,存在就使用,不存在就創(chuàng)建秦陋,用完后蔓彩,不關(guān)閉Statement對象,而是放置于Map<String, Statement>內(nèi)驳概,供下一次使用赤嚼。簡言之,就是重復(fù)使用Statement對象抡句。
BatchExecutor:執(zhí)行update(沒有select,JDBC批處理不支持select)杠愧,將所有sql都添加到批處理中(addBatch())待榔,等待統(tǒng)一執(zhí)行(executeBatch()),它緩存了多個Statement對象流济,每個Statement對象都是addBatch()完畢后锐锣,等待逐一執(zhí)行executeBatch()批處理。與JDBC批處理相同绳瘟。
作用范圍:Executor的這些特點雕憔,都嚴格限制在SqlSession生命周期范圍內(nèi)。
14.Mybatis中如何指定使用哪一種Executor執(zhí)行器糖声?
答:在Mybatis配置文件中斤彼,可以指定默認的ExecutorType執(zhí)行器類型,也可以手動給DefaultSqlSessionFactory的創(chuàng)建SqlSession的方法傳遞ExecutorType類型參數(shù)蘸泻。
15.Mybatis是否可以映射Enum枚舉類琉苇?
答:Mybatis可以映射枚舉類,不單可以映射枚舉類悦施,Mybatis可以映射任何對象到表的一列上并扇。映射方式為自定義一個TypeHandler,實現(xiàn)TypeHandler的setParameter()和getResult()接口方法抡诞。
TypeHandler有兩個作用穷蛹,一是完成從javaType至jdbcType的轉(zhuǎn)換,二是完成jdbcType至javaType的轉(zhuǎn)換昼汗,體現(xiàn)為setParameter()和getResult()兩個方法肴熏,分別代表設(shè)置sql問號占位符參數(shù)和獲取列查詢結(jié)果。
16.Mybatis映射文件中顷窒,如果A標(biāo)簽通過include引用了B標(biāo)簽的內(nèi)容扮超,請問,B標(biāo)簽?zāi)芊穸x在A標(biāo)簽的后面,還是說必須定義在A標(biāo)簽的前面出刷?
答:雖然Mybatis解析Xml映射文件是按照順序解析的璧疗,但是,被引用的B標(biāo)簽依然可以定義在任何地方馁龟,Mybatis都可以正確識別崩侠。
原理是,Mybatis解析A標(biāo)簽坷檩,發(fā)現(xiàn)A標(biāo)簽引用了B標(biāo)簽却音,但是B標(biāo)簽尚未解析到,尚不存在矢炼,此時系瓢,Mybatis會將A標(biāo)簽標(biāo)記為未解析狀態(tài),然后繼續(xù)解析余下的標(biāo)簽句灌,包含B標(biāo)簽夷陋,待所有標(biāo)簽解析完畢,Mybatis會重新解析那些被標(biāo)記為未解析的標(biāo)簽胰锌,此時再解析A標(biāo)簽時骗绕,B標(biāo)簽已經(jīng)存在,A標(biāo)簽也就可以正常解析完成了资昧。
17.簡述Mybatis的Xml映射文件和Mybatis內(nèi)部數(shù)據(jù)結(jié)構(gòu)之間的映射關(guān)系酬土?
答:Mybatis將所有Xml配置信息都封裝到All-In-One重量級對象Configuration內(nèi)部。在Xml映射文件中格带,<parameterMap>標(biāo)簽會被解析為ParameterMap對象撤缴,其每個子元素會被解析為ParameterMapping對象。
<resultMap>標(biāo)簽會被解析為ResultMap對象叽唱,其每個子元素會被解析為ResultMapping對象腹泌。每一個<select>、<insert>尔觉、<update>凉袱、<delete>標(biāo)簽均會被解析為MappedStatement對象,標(biāo)簽內(nèi)的sql會被解析為BoundSql對象侦铜。
18.為什么說Mybatis是半自動ORM映射工具专甩?它與全自動的區(qū)別在哪里?
答:Hibernate屬于全自動ORM映射工具钉稍,使用Hibernate查詢關(guān)聯(lián)對象或者關(guān)聯(lián)集合對象時涤躲,可以根據(jù)對象關(guān)系模型直接獲取,所以它是全自動的贡未。而Mybatis在查詢關(guān)聯(lián)對象或關(guān)聯(lián)集合對象時种樱,需要手動編寫sql來完成蒙袍,所以,稱之為半自動ORM映射工具嫩挤。