Mybatis 源码探究 (4) 将sql 语句中的#{id} 替换成 ‘?
Mybatis 源碼探究 (4) 將sql 語句中的#{id} 替換成 '?
出于好奇,然后就有了這篇文章啦。
源碼給我的感覺,是一座大山的感覺。曲曲折折的路很多,點進去就有可能出不來。
不過慢慢看下來,收貨是有的,對一些理解更為深刻了,而且越來越覺得數據結構是真的真的重要,底層的類,就沒有不用到數據結構的。
傳進來的參數text是 select t_user.id,t_user.username,t_user.password from t_user where t_user.id=#{id}
這里需要做的就是講#{id} 替換成 ?。
GenericTokenParser 類
package org.apache.ibatis.parsing;/*** @author Clinton Begin*/ public class GenericTokenParser {private final String openToken; // openToken: "#{"private final String closeToken; // closeToken: "}"// 這里實際調用的是TokenHandler的實現類 SqlSourceBuilder類中的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劃分為 字符數組 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 語句 重新賦值給 builder啦offset = start + openToken.length();// 定位到參數的開始位置 // 從 offset 索引開始搜索 "}" 出現的位置 賦給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" 然后添加進 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 {// 此時 handler.handleToken(expression.toString()) 返回值 實際就是 "? "// 但之中還做了其他操作,我們暫不分析。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);}//此時 返回的已是: 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 "?"; }在這個方法中,其返回值就是返回一個“?” 。buildParameterMapping(content) 做了一些操作,看起來像檢驗類型,我沒有完全看懂,不亂說。 parameterMappings.add(); 這個 parameterMappings 實際上是一個 private List parameterMappings = new ArrayList<>(); List的數據結構 存儲。這步操作肯定是有用的,但是我目前還沒有明白哈。
StringBuilder
- 一個可變的字符序列。 此類提供與StringBuffer兼容的 API,但不保證同步。 此類旨在用作StringBuffer替代品,用于在單個線程使用字符串緩沖區的地方(通常是這種情況)。 在可能的情況下,建議優先使用此類而不是StringBuffer因為在大多數實現下它會更快。
- StringBuilder上的主要操作是append和insert方法,它們被重載以接受任何類型的數據。 每個都有效地將給定的數據轉換為字符串,然后將該字符串的字符附加或插入到字符串構建器中。 append方法總是在構建器的末尾添加這些字符; insert方法在指定點添加字符。
- 例如,如果z指字符串生成器對象,其當前內容是“ start ”,則該方法調用z.append(“le”)將導致字符串生成器含有“ startle ”,而z.insert(4, “le”)會將字符串生成器更改為包含“ starlet ”。
- 通常,如果 sb 引用StringBuilder的實例,則sb.append(x)與sb.insert(sb.length(), x)具有相同的效果。
每個字符串生成器都有容量。 只要字符串生成器中包含的字符序列的長度不超過容量,就沒有必要分配新的內部緩沖區。 如果內部緩沖區溢出,它會自動變大。 - 多線程使用StringBuilder實例是不安全的。 如果需要此類同步,則建議使用StringBuffer 。
自言自語
雖然對于mybatis 仍然感覺什么都沒有懂,都只是出于好奇,去探究一下。
但是在這個過程中,我深刻的感受到了數據結構的重要性,底層的存儲不是Map 就是List 等等。
就像Mybatis 的一級緩存,二級緩存等等,他們的底層存儲就是依賴于不同的數據結構的。
看個好康的圖,放松一下啦
總結
以上是生活随笔為你收集整理的Mybatis 源码探究 (4) 将sql 语句中的#{id} 替换成 ‘?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mybatis 源码探究 (3)创建 S
- 下一篇: Java类的加载过程详解 面试高频!!!