1. #{}和${}的區(qū)別是什么?
{driver}會(huì)被靜態(tài)替換為 com.mysql.jdbc.Driver 姿染。
{} 是 sql 的參數(shù)占位符蹄皱,Mybatis 會(huì)將 sql 中的 #{} 替換為?號旧噪,在 sql 執(zhí)行前會(huì)使用 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> ,加上動(dòng)態(tài) sql 的 9 個(gè)標(biāo)簽概龄, trim|where|set|foreach|if|choose|when|otherwise|bind 等还惠,其中為 sql 片段標(biāo)簽,通過<include> 標(biāo)簽引入 sql 片段私杜, <selectKey> 為不支持自增的主鍵生成策略標(biāo)簽蚕键。
3. 最佳實(shí)踐中,通常一個(gè) Xml 映射文件歪今,都會(huì)寫一個(gè) Dao 接口 與之對應(yīng)嚎幸,請問,這個(gè) Dao 接口的工作原理是什么?Dao 接口里的 方法寄猩,參數(shù)不同時(shí)嫉晶,方法能重載嗎?
Dao 接口,就是人們常說的 Mapper 接口田篇,接口的全限名替废,就是映射文件中的 namespace 的值,接口的方法名泊柬,就是映射文件中 MappedStatement 的 id 值椎镣,接口方法內(nèi)的參數(shù),就是傳遞 給 sql 的參數(shù)兽赁。 Mapper 接口是沒有實(shí)現(xiàn)類的状答,當(dāng)調(diào)用接口方法時(shí),接口全限名+方法名拼接字符 串作為 key 值刀崖,可唯一定位一個(gè) MappedStatement 惊科,舉例: com.mybatis3.mappers.StudentDao.findStudentById ,可以唯一找到 namespace為 com.mybatis3.mappers.StudentDao 下面 id = findStudentById 的 MappedStatement 亮钦。在 Mybatis 中馆截,每一個(gè) <select> 、 <insert> 、 <update> 蜡娶、 <delete> 標(biāo)簽混卵,都會(huì)被解析為一個(gè) MappedStatement 對象。
Dao 接口里的方法窖张,是不能重載的幕随,因?yàn)槭侨廾?方法名的保存和尋找策略。
Dao 接口的工作原理是 JDK 動(dòng)態(tài)代理宿接,Mybatis 運(yùn)行時(shí)會(huì)使用 JDK 動(dòng)態(tài)代理為 Dao 接口生成代 理 proxy 對象合陵,代理對象 proxy 會(huì)攔截接口方法,轉(zhuǎn)而執(zhí)行 MappedStatement 所代表的 sql澄阳,然后 將 sql 執(zhí)行結(jié)果返回拥知。
4. Mybatis 是如何進(jìn)行分?的?分?插件的原理是什么?
Mybatis 使用 RowBounds 對象進(jìn)行分?,它是針對 ResultSet 結(jié)果集執(zhí)行的內(nèi)存分?碎赢,而 非物理分?低剔,可以在 sql 內(nèi)直接書寫帶有物理分?的參數(shù)來完成物理分?功能,也可以使用分? 插件來完成物理分?肮塞。
分?插件的基本原理是使用 Mybatis 提供的插件接口襟齿,實(shí)現(xiàn)自定義插件,在插件的攔截方法內(nèi)攔 截待執(zhí)行的 sql枕赵,然后重寫 sql猜欺,根據(jù) dialect 方言,添加對應(yīng)的物理分?語句和物理分?參數(shù)拷窜。
5. 簡述 Mybatis 的插件運(yùn)行原理开皿,以及如何編寫一個(gè)插件。
Mybatis 僅可以編寫針對ParameterHandler 篮昧、 ResultSetHandler赋荆、StatementHandler 、 Executor 這 4 種接口的插件懊昨,
Mybatis 使用 JDK 的動(dòng)態(tài)代理窄潭,為需要攔截的接口生成代理對象以實(shí)現(xiàn)接口方法攔截功能,每當(dāng) 執(zhí)行這 4 種接口對象的方法時(shí)酵颁,就會(huì)進(jìn)入攔截方法嫉你,具體就是 InvocationHandler 的 invoke() 方法,當(dāng)然躏惋,只會(huì)攔截那些你指定需要攔截的方法幽污。
實(shí)現(xiàn) Mybatis 的 Interceptor 接口并復(fù)寫 intercept() 方法,然后在給插件編寫注解其掂,指定要攔截哪 一個(gè)接口的哪些方法即可油挥,記住潦蝇,別忘了在配置文件中配置你編寫的插件款熬。
6. Mybatis 執(zhí)行批量插入深寥,能返回?cái)?shù)據(jù)庫主鍵列表嗎?
能,JDBC 都能贤牛,Mybatis 當(dāng)然也能惋鹅。
7. Mybatis 動(dòng)態(tài) sql 是做什么的?都有哪些動(dòng)態(tài) sql?能簡述一 下動(dòng)態(tài) sql 的執(zhí)行原理不?
Mybatis 動(dòng)態(tài) sql 可以讓我們在 Xml 映射文件內(nèi),以標(biāo)簽的形式編寫動(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ù)對象中計(jì)算表達(dá)式的值武鲁,根據(jù)表達(dá)式的值動(dòng)態(tài)拼接 sql,以此來完成動(dòng)態(tài) sql 的功能蝠检。
8. Mybatis 是如何將 sql 執(zhí)行結(jié)果封裝為目標(biāo)對象并返回的?都有哪些映射形式?
第一種是使用 <resultMap> 標(biāo)簽沐鼠,逐一定義列名和對象屬性名之間的映射關(guān)系。第二種是使 用 sql 列的別名功能叹谁,將列別名書寫為對象屬性名饲梭,比如 T_NAME AS NAME,對象屬性名一般 是 name焰檩,小寫憔涉,但是列名不區(qū)分大小寫,Mybatis 會(huì)忽略列名大小寫析苫,智能找到與之對應(yīng)對象 屬性名兜叨,你甚至可以寫成 T_NAME AS NaMe,Mybatis 一樣可以正常工作衩侥。
有了列名與屬性名的映射關(guān)系后浪腐,Mybatis 通過反射創(chuàng)建對象,同時(shí)使用反射給對象的屬性逐一 賦值并返回顿乒,那些找不到映射關(guān)系的屬性议街,是無法完成賦值的。
9. Mybatis 能執(zhí)行一對一璧榄、一對多的關(guān)聯(lián)查詢嗎?都有哪些實(shí)現(xiàn) 方式特漩,以及它們之間的區(qū)別。
能骨杂,Mybatis 不僅可以執(zhí)行一對一涂身、一對多的關(guān)聯(lián)查詢,還可以執(zhí)行多對一搓蚪,多對多的關(guān)聯(lián) 查詢蛤售,多對一查詢,其實(shí)就是一對一查詢,只需要把 selectOne() 修改為 selectList() 即可;多對 多查詢悴能,其實(shí)就是一對多查詢揣钦,只需要把 selectOne() 修改為 selectList() 即可。
關(guān)聯(lián)對象查詢漠酿,有兩種實(shí)現(xiàn)方式冯凹,一種是單獨(dú)發(fā)送一個(gè) sql 去查詢關(guān)聯(lián)對象,賦給主對象炒嘲,然后 返回主對象宇姚。另一種是使用嵌套查詢,嵌套查詢的含義為使用 join 查詢夫凸,一部分列是 A 對象的屬 性值浑劳,另外一部分列是關(guān)聯(lián)對象 B 的屬性值,好處是只發(fā)一個(gè) sql 查詢夭拌,就可以把主對象和其關(guān) 聯(lián)對象查出來呀洲。
那么問題來了,join 查詢出來 100 條記錄啼止,如何確定主對象是 5 個(gè)道逗,而不是 100 個(gè)?其去重復(fù)的 原理是 <resultMap> 標(biāo)簽內(nèi)的 <id> 子標(biāo)簽,指定了唯一確定一條記錄的 id 列献烦,Mybatis 根據(jù)列 值來完成 100 條記錄的去重復(fù)功能滓窍, <id> 可以有多個(gè),代表了聯(lián)合主鍵的語意巩那。
同樣主對象的關(guān)聯(lián)對象吏夯,也是根據(jù)這個(gè)原理去重復(fù)的,盡管一般情況下即横,只有主對象會(huì)有重復(fù)記 錄噪生,關(guān)聯(lián)對象一般不會(huì)重復(fù)。
舉例:下面 join 查詢出來 6 條記錄东囚,一跺嗽、二列是 Teacher 對象列,第三列為 Student 對象列页藻, Mybatis 去重復(fù)處理后桨嫁,結(jié)果為 1 個(gè)老師 6 個(gè)學(xué)生,而不是 6 個(gè)老師 6 個(gè)學(xué)生份帐。
10. Mybatis 是否支持延遲加載?如果支持璃吧,它的實(shí)現(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)方法時(shí)毡咏,進(jìn)入攔截器方法,比 如調(diào)用 a.getB().getName() 务冕,攔截器 invoke() 方法發(fā)現(xiàn) a.getB() 是 null 值,那么就會(huì)單獨(dú)發(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 不是必須的窗怒,只是最佳實(shí)踐而已映跟。
原因就是 namespace+id 是作為 Map<String, MappedStatement> 的 key 使用的,如果沒有 namespace扬虚,就剩下 id努隙,那么,id 重復(fù)會(huì)導(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钦椭,就開啟一個(gè) 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())粪摘,它緩存了多個(gè) Statement 對 象,每個(gè) Statement 對象都是 addBatch()完畢后绍坝,等待逐一執(zhí)行 executeBatch()批處理徘意。與 JDBC 批處理相同。
作用范圍:Executor 的這些特點(diǎn)轩褐,都嚴(yán)格限制在 SqlSession 生命周期范圍內(nèi)映砖。
14. Mybatis 中如何指定使用哪一種 Executor 執(zhí)行器?
在 Mybatis 配置文件中,可以指定默認(rèn)的 ExecutorType 執(zhí)行器類型灾挨,也可以手動(dòng)給
DefaultSqlSessionFactory 的創(chuàng)建 SqlSession 的方法傳遞 ExecutorType 類型參數(shù)邑退。
15. Mybatis 是否可以映射 Enum 枚舉類?
Mybatis 可以映射枚舉類,不單可以映射枚舉類劳澄,Mybatis 可以映射任何對象到表的一列 上地技。映射方式為自定義一個(gè) TypeHandler ,實(shí)現(xiàn) TypeHandler 的 setParameter() 和 getResult() 接口方法秒拔。 TypeHandler 有兩個(gè)作用莫矗,一是完成從 javaType 至 jdbcType 的轉(zhuǎn)換,二是完成 jdbcType 至 javaType 的轉(zhuǎn)換砂缩,體現(xiàn)為 setParameter() 和 getResult() 兩個(gè)方法作谚,分別代表設(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)簽尚未解析到临庇,尚不存 在,此時(shí)昵慌,Mybatis 會(huì)將 A 標(biāo)簽標(biāo)記為未解析狀態(tài)假夺,然后繼續(xù)解析余下的標(biāo)簽,包含 B 標(biāo)簽废离,待 所有標(biāo)簽解析完畢侄泽,Mybatis 會(huì)重新解析那些被標(biāo)記為未解析的標(biāo)簽礁芦,此時(shí)再解析 A 標(biāo)簽時(shí)蜻韭,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)簽會(huì)被解析為 ParameterMap 對象未状,其每個(gè)子元素會(huì)被解析為 ParameterMapping 對象俯画。 <resultMap> 標(biāo)簽會(huì)被解析為 ResultMap 對象,其每個(gè)子元素會(huì)被解 析為 ResultMapping 對象司草。每一個(gè) <select>??<insert>??<update>??<delete> 標(biāo)簽均會(huì)被解析為
MappedStatement 對象艰垂,標(biāo)簽內(nèi)的 sql 會(huì)被解析為 BoundSql 對象。
18. 為什么說 Mybatis 是半自動(dòng) ORM 映射工具?它與全自動(dòng)的 區(qū)別在哪里?
Hibernate 屬于全自動(dòng) ORM 映射工具埋虹,使用 Hibernate 查詢關(guān)聯(lián)對象或者關(guān)聯(lián)集合對象時(shí)猜憎, 可以根據(jù)對象關(guān)系模型直接獲取,所以它是全自動(dòng)的搔课。而 Mybatis 在查詢關(guān)聯(lián)對象或關(guān)聯(lián)集合對 象時(shí)胰柑,需要手動(dòng)編寫 sql 來完成,所以爬泥,稱之為半自動(dòng) ORM 映射工具柬讨。