sql字符串拼接_Mybatis的SqlSession执行sql过程
上一篇分析了SqlSession執(zhí)行sql的過(guò)程,其中并沒(méi)有分析sql是從哪里來(lái)的,今天就來(lái)仔細(xì)分析下。
Sql來(lái)源
從上一篇的最后一步執(zhí)行sql那里倒推sql的來(lái)源,源碼主要過(guò)程如下圖:
可以看到最后是通過(guò)BoundSql直接獲取的sql,然后往前倒推最后發(fā)現(xiàn)是通過(guò)MappedStatement的getBoundSql方法返回的。MappedStatement在之前分析mapper的時(shí)候知道一個(gè)執(zhí)行sql對(duì)應(yīng)一個(gè)MappedStatement對(duì)象,它封裝有mybatis中需要執(zhí)行一條sql的所有信息,所以從這里獲取也是理所應(yīng)當(dāng)?shù)摹?/p>
MappedStatement的getBoundSql方法
那么就來(lái)看下MappedStatement的getBoundSql方法吧,源碼如下圖:
首先是右邊MappedStatement的getBoundSql方法,這次改了下sql傳遞了兩個(gè)參數(shù),但是對(duì)程序基本沒(méi)什么影響。可以看到BoundSql是通過(guò)SqlSource創(chuàng)建的,通過(guò)debug知道是DynamicSqlSource對(duì)象。
左邊是DynamicSqlSource的getBoundSql方法,可以先看后面的創(chuàng)建BoundSql對(duì)象,是通過(guò)SqlSource初始化的,而創(chuàng)建SqlSource對(duì)象需要的第一個(gè)參數(shù)是通過(guò)context.getSql()得到的。
而在getBoundSql方法第一步是初始化了context,從上圖的debug可以看到目前context的信息,bindings屬性已經(jīng)通過(guò)初始化把參數(shù)設(shè)置進(jìn)去,而sqlBuilder還是一個(gè)空字符串。關(guān)鍵代碼就在當(dāng)前打斷點(diǎn)的“rootSqlNode.apply(context);”這行代碼。
從上圖可以看到rootSqlNode是一個(gè)MixedSqlNode對(duì)象,MixedSqlNode對(duì)象的apply方法是遍歷屬性contents的所有元素并執(zhí)行它們的apply方法,可以看到contents只有一個(gè)TextSqlNode對(duì)象的元素。所以最終來(lái)到TextSqlNode對(duì)象。
TextSqlNode對(duì)象的text屬性保存的就是還沒(méi)有進(jìn)行處理的sql。
TextSqlNode處理sql
TextSqlNode處理sql的源碼如下圖:
右側(cè)是TextSqlNode的apply方法,參數(shù)context就是上一步需要的context,他里面包含有請(qǐng)求參數(shù),從上一張?jiān)创a圖可以知道它的屬性sqlBuilder此時(shí)還是一個(gè)空字符串,而apply就是去拼接sqlBuilder。
apply方法首先創(chuàng)建了一個(gè)GenericTokenParser對(duì)象,GenericTokenParser對(duì)象有三個(gè)屬性openToken、closeToken、handler。其中openToken、closeToken分別對(duì)應(yīng)字符串”${”、”}”,而handler的屬性context對(duì)應(yīng)傳遞進(jìn)來(lái)的context,injectionFilter屬性是TextSqlNode的injectionFilter。apply第二行代碼“context.appendSql(parser.parse(text));”可以看出來(lái)parser.parse(text)就是生成sql的代碼。其中text是TextSqlNode中沒(méi)有處理的原始sql。
所以sql生成在GenericTokenParser的parse方法,parse方法的關(guān)鍵源碼如上圖左側(cè)。主要步驟分析如下:
1、從sql找到第一個(gè)“${”位置start,獲取到元素sql(text)的字符數(shù)組src;
2、把src從開(kāi)始到start處的字符拼接到結(jié)果builder上,也就是把“${”的內(nèi)容拼接到結(jié)果上;
3、從start+2的位置開(kāi)始找“}”的位置end,從src數(shù)組里取出“${”、“}”之間的字符組成字符串并根據(jù)字符串從hander中取出字符串對(duì)應(yīng)的參數(shù)值。并把值拼接到結(jié)果builder上。
最后還有一點(diǎn)代碼沒(méi)有截取到“start = text.indexOf(openToken, offset);
} while (start > -1);”也就是第4步。
4、繼續(xù)找到下一個(gè)“${”并拼接。最終組成完整的sql。
實(shí)際上GenericTokenParser的parse方法還是比較簡(jiǎn)單的,就是把傳遞進(jìn)來(lái)的參數(shù)text中openToken、closeToken中間的內(nèi)容替換成對(duì)應(yīng)的參數(shù)。
總結(jié)
從之前的分析我們知道一個(gè)MappedStatement對(duì)應(yīng)一個(gè)sql,那么如何從MappedStatement獲取組裝好的sql呢,實(shí)際上是MappedStatement的屬性sqlSource。
而sqlSource實(shí)際上是依靠rootSqlNode,在之前我們分析過(guò)解析mapper文件是把sql生成了嵌套的各種SqlNode子類。今天就看到他們的使用了。不過(guò)今天的算是比較簡(jiǎn)單的,后面來(lái)一個(gè)稍微復(fù)雜一點(diǎn)的看看sql的解析過(guò)程。
Java程序員日常學(xué)習(xí)筆記,如理解有誤歡迎各位交流討論!
總結(jié)
以上是生活随笔為你收集整理的sql字符串拼接_Mybatis的SqlSession执行sql过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Intel处理器明年又要换LGA1851
- 下一篇: wince2秒快速启动TOC分析