Mybatis 源码探究 (4) 将sql 语句中的#{id} 替换成 ‘?
Mybatis 源碼探究 (4) 將sql 語(yǔ)句中的#{id} 替換成 '?
出于好奇,然后就有了這篇文章啦。
源碼給我的感覺(jué),是一座大山的感覺(jué)。曲曲折折的路很多,點(diǎn)進(jìn)去就有可能出不來(lái)。
不過(guò)慢慢看下來(lái),收貨是有的,對(duì)一些理解更為深刻了,而且越來(lái)越覺(jué)得數(shù)據(jù)結(jié)構(gòu)是真的真的重要,底層的類(lèi),就沒(méi)有不用到數(shù)據(jù)結(jié)構(gòu)的。
傳進(jìn)來(lái)的參數(shù)text是 select t_user.id,t_user.username,t_user.password from t_user where t_user.id=#{id}
這里需要做的就是講#{id} 替換成 ?。
GenericTokenParser 類(lèi)
package org.apache.ibatis.parsing;/*** @author Clinton Begin*/ public class GenericTokenParser {private final String openToken; // openToken: "#{"private final String closeToken; // closeToken: "}"// 這里實(shí)際調(diào)用的是TokenHandler的實(shí)現(xiàn)類(lèi) SqlSourceBuilder類(lèi)中的ParameterMappingTokenHandlerprivate final TokenHandler handler; public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {this.openToken = openToken;this.closeToken = closeToken;this.handler = handler;}public String parse(String text) {if (text == null || text.isEmpty()) {return "";}// search open token// 這里就是找到 "#{" 的起始位置int start = text.indexOf(openToken);if (start == -1) {return text;}// 將text劃分為 字符數(shù)組 char[] src = text.toCharArray();int offset = 0;//StringBuilder 文末有講 可理解為 StringBuffer final StringBuilder builder = new StringBuilder();StringBuilder expression = null;do {if (start > 0 && src[start - 1] == '\\') {// this open token is escaped. remove the backslash and continue.builder.append(src, offset, start - offset - 1).append(openToken);offset = start + openToken.length();} else {// found open token. let's search close token.if (expression == null) {expression = new StringBuilder();} else {expression.setLength(0);}builder.append(src, offset, start - offset);//"select t_user.id,t_user.username,t_user.password from t_user where t_user.id="// 可以理解為 將去除了#{} 的sql 語(yǔ)句 重新賦值給 builder啦offset = start + openToken.length();// 定位到參數(shù)的開(kāi)始位置 // 從 offset 索引開(kāi)始搜索 "}" 出現(xiàn)的位置 賦給end int end = text.indexOf(closeToken, offset);while (end > -1) {if (end > offset && src[end - 1] == '\\') {// this close token is escaped. remove the backslash and continue.expression.append(src, offset, end - offset - 1).append(closeToken);offset = end + closeToken.length();end = text.indexOf(closeToken, offset);} else {// 取到"id" 然后添加進(jìn) expressionexpression.append(src, offset, end - offset);break;}}if (end == -1) {// close token was not found.builder.append(src, start, src.length - start);offset = src.length;} else {// 此時(shí) handler.handleToken(expression.toString()) 返回值 實(shí)際就是 "? "// 但之中還做了其他操作,我們暫不分析。builder.append(handler.handleToken(expression.toString()));offset = end + closeToken.length();}}start = text.indexOf(openToken, offset);} while (start > -1);if (offset < src.length) {builder.append(src, offset, src.length - offset);}//此時(shí) 返回的已是: select t_user.id,t_user.username,t_user.password from t_user where t_user.id=?return builder.toString();} }handler.handleToken(expression.toString())
@Override public String handleToken(String content) {parameterMappings.add(buildParameterMapping(content));return "?"; }在這個(gè)方法中,其返回值就是返回一個(gè)“?” 。buildParameterMapping(content) 做了一些操作,看起來(lái)像檢驗(yàn)類(lèi)型,我沒(méi)有完全看懂,不亂說(shuō)。 parameterMappings.add(); 這個(gè) parameterMappings 實(shí)際上是一個(gè) private List parameterMappings = new ArrayList<>(); List的數(shù)據(jù)結(jié)構(gòu) 存儲(chǔ)。這步操作肯定是有用的,但是我目前還沒(méi)有明白哈。
StringBuilder
- 一個(gè)可變的字符序列。 此類(lèi)提供與StringBuffer兼容的 API,但不保證同步。 此類(lèi)旨在用作StringBuffer替代品,用于在單個(gè)線程使用字符串緩沖區(qū)的地方(通常是這種情況)。 在可能的情況下,建議優(yōu)先使用此類(lèi)而不是StringBuffer因?yàn)樵诖蠖鄶?shù)實(shí)現(xiàn)下它會(huì)更快。
- StringBuilder上的主要操作是append和insert方法,它們被重載以接受任何類(lèi)型的數(shù)據(jù)。 每個(gè)都有效地將給定的數(shù)據(jù)轉(zhuǎn)換為字符串,然后將該字符串的字符附加或插入到字符串構(gòu)建器中。 append方法總是在構(gòu)建器的末尾添加這些字符; insert方法在指定點(diǎn)添加字符。
- 例如,如果z指字符串生成器對(duì)象,其當(dāng)前內(nèi)容是“ start ”,則該方法調(diào)用z.append(“l(fā)e”)將導(dǎo)致字符串生成器含有“ startle ”,而z.insert(4, “l(fā)e”)會(huì)將字符串生成器更改為包含“ starlet ”。
- 通常,如果 sb 引用StringBuilder的實(shí)例,則sb.append(x)與sb.insert(sb.length(), x)具有相同的效果。
每個(gè)字符串生成器都有容量。 只要字符串生成器中包含的字符序列的長(zhǎng)度不超過(guò)容量,就沒(méi)有必要分配新的內(nèi)部緩沖區(qū)。 如果內(nèi)部緩沖區(qū)溢出,它會(huì)自動(dòng)變大。 - 多線程使用StringBuilder實(shí)例是不安全的。 如果需要此類(lèi)同步,則建議使用StringBuffer 。
自言自語(yǔ)
雖然對(duì)于mybatis 仍然感覺(jué)什么都沒(méi)有懂,都只是出于好奇,去探究一下。
但是在這個(gè)過(guò)程中,我深刻的感受到了數(shù)據(jù)結(jié)構(gòu)的重要性,底層的存儲(chǔ)不是Map 就是List 等等。
就像Mybatis 的一級(jí)緩存,二級(jí)緩存等等,他們的底層存儲(chǔ)就是依賴(lài)于不同的數(shù)據(jù)結(jié)構(gòu)的。
看個(gè)好康的圖,放松一下啦
總結(jié)
以上是生活随笔為你收集整理的Mybatis 源码探究 (4) 将sql 语句中的#{id} 替换成 ‘?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Mybatis 源码探究 (3)创建 S
- 下一篇: 小学五年级就已经开始编程啦吗???