Mybatis3.5.1源碼分析
- Mybatis-SqlSessionFactoryBuilder微峰,XMLConfigBuilder交惯,XPathParser源碼解析
- Mybatis-Configuration源碼解析
- Mybatis-事務(wù)對象源碼解析
- Mybatis-數(shù)據(jù)源源碼解析
- Mybatis緩存策略源碼解析
- Mybatis-DatabaseIdProvider源碼解析
- Mybatis-TypeHandler源碼解析
- Mybatis-Reflector源碼解析
- Mybatis-ObjectFactory扮饶,ObjectWrapperFactory源碼分析
- Mybatis-Mapper各類標簽封裝類源碼解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
- Mybatis-MapperAnnotationBuilder源碼分析
- [Mybatis-MetaObject,MetaClass源碼解析]http://www.reibang.com/p/f51fa552f30a)
- Mybatis-LanguageDriver源碼解析
- Mybatis-SqlSource源碼解析
- Mybatis-SqlNode源碼解析
- Mybatis-KeyGenerator源碼解析
- Mybatis-Executor源碼解析
- Mybatis-ParameterHandler源碼解析
- Mybatis-StatementHandler源碼解析
- Mybatis-DefaultResultSetHandler(一)源碼解析
- Mybatis-DefaultResultSetHandler(二)源碼解析
- Mybatis-ResultHandler,Cursor,RowBounds 源碼分析
- Mybatis-MapperProxy源碼解析
- Mybatis-SqlSession源碼解析
- Mybatis-Interceptor源碼解析
SqlSource
作用于XMLLanguageDriver
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.mapping;
/**
* Represents the content of a mapped statement read from an XML file or an annotation.
* It creates the SQL that will be passed to the database out of the input parameter received from the user.
* <p>
* 構(gòu)建動態(tài)SQL
* </p>
* <p>
* 代表的內(nèi)容映射語句讀取XML文件或一個注解它創(chuàng)建的SQL將被傳遞到數(shù)據(jù)庫收到用戶的輸入?yún)?shù)
* </p>
* <p>
* 參考博客:<a herf='https://blog.csdn.net/LHQJ1992/article/details/90320639'>https://blog.csdn.net/LHQJ1992/article/details/90320639</a>
* </p>
* @author Clinton Begin
*/
public interface SqlSource {
/**
* 根據(jù) {@code parameterObject} 構(gòu)建動態(tài)SQL
* @param parameterObject 參數(shù)對象
* @return {@link BoundSql} 對象
*/
BoundSql getBoundSql(Object parameterObject);
}
DynamicSqlSource
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;
/**
* 只創(chuàng)建DynamicSqlSource對象。完整可執(zhí)行的sql逆皮,需要在調(diào)用getBoundSql(Parameter)方法時才能組裝完成。
* @author Clinton Begin
*/
public class DynamicSqlSource implements SqlSource {
/**
* mybatis配置信息
*/
private final Configuration configuration;
/**
* DML標簽下的子節(jié)點螺男,封裝成了MixedSqlNode
*/
private final SqlNode rootSqlNode;
/**
*
* @param configuration mybatis配置信息
* @param rootSqlNode DML標簽下的子節(jié)點,封裝成了MixedSqlNode
*/
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
/**
* 構(gòu)建動態(tài)SQL上下文,DynamicContext主要用于記錄解析動態(tài)SQL語句之后產(chǎn)生的SQL語句片段纵穿,
* 可以認為它是一個用于記錄動態(tài)SQL語句解析結(jié)果的容器
*/
DynamicContext context = new DynamicContext(configuration, parameterObject);
//解析 SQL 片段下隧,并將解析結(jié)果存儲到 DynamicContext 中
rootSqlNode.apply(context);
// 解析 SQL 語句,并構(gòu)建 StaticSqlSource谓媒,在此過程中將 sql 語句中的占位符 #{} 替換為問號 ?淆院,并為每個占位符構(gòu)建相應(yīng)的 ParameterMapping
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
// 從SqlNode 上下文中得到完成的originalSql后開始構(gòu)建SqlSource
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
//調(diào)用 StaticSqlSource 的 getBoundSql 獲取 BoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
//將 DynamicContext 的 ContextMap 中的內(nèi)容拷貝到 BoundSql 中
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
}
ProviderSqlSource
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.builder.annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.apache.ibatis.annotations.Lang;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
/**
* SQL提供者的SQL源
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class ProviderSqlSource implements SqlSource {
/**
* Mybatis全局配置信息
*/
private final Configuration configuration;
/**
* SQL腳本提供者類
*/
private final Class<?> providerType;
/**
* 語言驅(qū)動
*/
private final LanguageDriver languageDriver;
/**
* SQL provider 的對應(yīng)映射器方法的方法對象
*/
private Method providerMethod;
/**
* SQL provider 的對應(yīng)映射器方法的方法對象的SQL腳本參數(shù)名數(shù)組
*/
private String[] providerMethodArgumentNames;
/**
* SQL provider 的對應(yīng)映射器方法的方法對象的參數(shù)類型數(shù)組
*/
private Class<?>[] providerMethodParameterTypes;
/**
* Sql provider 方法的上下文對象,封裝著映射接口類,映射接口類方法對象句惯, 數(shù)據(jù)庫ID
* <p>
* 在 SQL provider 的對應(yīng)映射器方法的方法對象中有這個ProvideContext的參數(shù)類型時土辩,才不會為null
* </p>
*/
private ProviderContext providerContext;
/**
* SQL provider 的對應(yīng)映射器方法的方法對象的ProvideContext類型參數(shù)的索引位置
* <p>
* 在 SQL provider 的對應(yīng)映射器方法的方法對象中有這個ProvideContext的參數(shù)類型時支救,才不會為null
* </p>
*/
private Integer providerContextIndex;
/**
* @deprecated Please use the {@link #ProviderSqlSource(Configuration, Object, Class, Method)} instead of this.
*/
@Deprecated
public ProviderSqlSource(Configuration configuration, Object provider) {
this(configuration, provider, null, null);
}
/**
* @since 3.4.5
*/
/**
*
* @param configuration Mybatis全局配置信息
* @param provider SQL provider 注解對象
* @param mapperType 映射接口類
* @param mapperMethod 映射接口類方法對象
*/
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
String providerMethodName;
try {
this.configuration = configuration;
//獲取mapperMethod的Lang注解
Lang lang = mapperMethod == null ? null : mapperMethod.getAnnotation(Lang.class);
//獲取Lang注解對象指定的語言驅(qū)動,如果沒有指定的話拷淘,默認使用XMLLanguageDriver
this.languageDriver = configuration.getLanguageDriver(lang == null ? null : lang.value());
//通過反射獲取SQL provider注解的type方法指定的SQL腳本提供者類
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
//通過反射獲取SQL provider注解的method方法指定的SQL腳本提供者類的方法的映射方法名
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
//如果方法名不是空字符 且 SQL腳本提供者是ProviderMethoResolver的子類
if (providerMethodName.length() == 0 && ProviderMethodResolver.class.isAssignableFrom(this.providerType)) {
/**
* 構(gòu)建providerType的實例對象各墨,強轉(zhuǎn)成ProviderMethodResolver類,構(gòu)建一個ProviderContext對象启涯,
* 傳入resolverMethod方法得到對應(yīng)的Sql provider 方法贬堵,賦值給providerMethod
*/
this.providerMethod = ((ProviderMethodResolver) this.providerType.getDeclaredConstructor().newInstance())
.resolveMethod(new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId()));
}
//如果provideMethod方法為null
if (this.providerMethod == null) {
//如果映射方法名為空字符串,就使用默認使用'provideSql'充當映射方法名;否則使用原來的
providerMethodName = providerMethodName.length() == 0 ? "provideSql" : providerMethodName;
//獲取SQL腳本提供者類的所有方法
for (Method m : this.providerType.getMethods()) {
//如果m等于映射方法名 且 m返回類型是CharSequence的子類
if (providerMethodName.equals(m.getName()) && CharSequence.class.isAssignableFrom(m.getReturnType())) {
//如果providerMethod不為null结洼,該判斷意味著出現(xiàn)多個匹配的方法
if (this.providerMethod != null) {
//拋出異常
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
+ providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName()
+ "'. Sql provider method can not overload.");
}
//將m賦值給providerMethod
this.providerMethod = m;
}
}
}
} catch (BuilderException e) {
throw e;
} catch (Exception e) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Cause: " + e, e);
}
//如果provideMethod為null,意味著在providerType中沒有找到匹配方法
if (this.providerMethod == null) {
//拋出異常
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
}
//ParamNameResolver:參數(shù)名稱解析器黎做,用于構(gòu)建sql腳本參數(shù)名+參數(shù)對象映射集合
//通過ParamNameResolver獲取sql腳本的參數(shù)名并賦值給providerMethodArgumentNames
this.providerMethodArgumentNames = new ParamNameResolver(configuration, this.providerMethod).getNames();
//獲取providerMethod的參數(shù)類型數(shù)組賦值給providerMethodParameterTypes
this.providerMethodParameterTypes = this.providerMethod.getParameterTypes();
//遍歷參數(shù)類型數(shù)組
for (int i = 0; i < this.providerMethodParameterTypes.length; i++) {
//獲取參數(shù)
Class<?> parameterType = this.providerMethodParameterTypes[i];
//ProviderContext:Sql provider 方法的上下文對象,封裝著映射接口類,映射接口類方法對象松忍, 數(shù)據(jù)庫ID
//如果參數(shù)類型為ProviderContext類型
if (parameterType == ProviderContext.class) {
//如果providerContext不為null,這里意味著providerContext在方法中出現(xiàn)了多個
if (this.providerContext != null) {
//拋出異常
throw new BuilderException("Error creating SqlSource for SqlProvider. ProviderContext found multiple in SqlProvider method ("
+ this.providerType.getName() + "." + providerMethod.getName()
+ "). ProviderContext can not define multiple in SqlProvider method argument.");
}
//新建一個ProviderContext賦值給providerContext
this.providerContext = new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId());
//將ProvideContext類型參數(shù)的索引位置賦值給providerContextIndex
this.providerContextIndex = i;
}
}
}
/**
* 傳入 {@code parameterObject} 蒸殿,然后獲取參數(shù)映射與可執(zhí)行SQL封裝類對象
* @param parameterObject 參數(shù)對象
* @return 已綁定參數(shù)對象的參數(shù)映射與可執(zhí)行SQL封裝類對象
*/
@Override
public BoundSql getBoundSql(Object parameterObject) {
//構(gòu)建動態(tài)SQL,并綁定參數(shù)對象
SqlSource sqlSource = createSqlSource(parameterObject);
//傳入?yún)?shù)對象鸣峭,然后獲取參數(shù)映射與可執(zhí)行SQL封裝類對象宏所,然后返回出去
return sqlSource.getBoundSql(parameterObject);
}
/**
* 構(gòu)建動態(tài)SQL,并綁定參數(shù)對象
* @param parameterObject 參數(shù)對象
* @return SQL源
*/
private SqlSource createSqlSource(Object parameterObject) {
try {
//綁定參數(shù)數(shù)量等于 SQL provider 的對應(yīng)映射器方法的方法對象的參數(shù)類型數(shù)組長度 -
// 一個providerContext類型參數(shù)摊溶,如果providerContext為null楣铁,就是-0,否則就是-1
int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);
String sql;
//如果SQL provider 的對應(yīng)映射器方法的方法對象的參數(shù)類型數(shù)組長度為0時
if (providerMethodParameterTypes.length == 0) {
//執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法得到Sql腳本
sql = invokeProviderMethod();
//如果綁定的參數(shù)數(shù)量為0
} else if (bindParameterCount == 0) {
//執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法更扁,并傳入providerContext, 得到Sql腳本
sql = invokeProviderMethod(providerContext);
//如果綁定參數(shù)數(shù)量為1 且 第一個非ProviderContext類型的參數(shù)其參數(shù)類型屬于parameterObject的父類或者同一個類型
} else if (bindParameterCount == 1
&& (parameterObject == null || providerMethodParameterTypes[providerContextIndex == null || providerContextIndex == 1 ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
/**
* 轉(zhuǎn)換 SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組赫冬,然后執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法浓镜,
* 并傳入該參數(shù)對象數(shù)組,得到SQL腳本
*/
sql = invokeProviderMethod(extractProviderMethodArguments(parameterObject));
//如果參數(shù)對象是Map的子類
} else if (parameterObject instanceof Map) {
//強制轉(zhuǎn)換參數(shù)對象為Map<String,Object>類型劲厌,并賦值params
@SuppressWarnings("unchecked")
Map<String, Object> params = (Map<String, Object>) parameterObject;
/**
* 轉(zhuǎn)換 SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組膛薛,然后執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法,
* 并傳入該參數(shù)對象數(shù)組补鼻,得到SQL腳本
*/
sql = invokeProviderMethod(extractProviderMethodArguments(params, providerMethodArgumentNames));
} else {
//如果provideMethod出現(xiàn)多個參數(shù)(不包含ProviderContext)哄啄,就會拋出異常.
throw new BuilderException("Error invoking SqlProvider method ("
+ providerType.getName() + "." + providerMethod.getName()
+ "). Cannot invoke a method that holds "
+ (bindParameterCount == 1 ? "named argument(@Param)" : "multiple arguments")
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
}
//獲取paramterObject的參數(shù)類型并賦值給parameterType
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
//通過語音驅(qū)動構(gòu)建動態(tài)SQL
return languageDriver.createSqlSource(configuration, sql, parameterType);
} catch (BuilderException e) {
throw e;
} catch (Exception e) {
throw new BuilderException("Error invoking SqlProvider method ("
+ providerType.getName() + "." + providerMethod.getName()
+ "). Cause: " + e, e);
}
}
/**
* 轉(zhuǎn)換 SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組
* @param parameterObject 參數(shù)對象
* @return 存放參數(shù)的object類型數(shù)組對象
*/
private Object[] extractProviderMethodArguments(Object parameterObject) {
//如果方法中存在providerContext類型參數(shù)
if (providerContext != null) {
//定義一個長度為2的Object類型數(shù)組
Object[] args = new Object[2];
//將parameterObject賦值給非ProviderConetxt類型的參數(shù)索引位置的元素
args[providerContextIndex == 0 ? 1 : 0] = parameterObject;
//將providerContext賦值給ProviderConetxt類型的參數(shù)索引位置的元素
args[providerContextIndex] = providerContext;
//返回args
return args;
//如果方法中不存在providerContext類型參數(shù)
} else {
//新建一個Object類型數(shù)組對象,并將parameterObject添加到object類型數(shù)組對象中
return new Object[] { parameterObject };
}
}
/**
* 轉(zhuǎn)換 SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組
* @param params 參數(shù)對象
* @param argumentNames SQL provider 的對應(yīng)映射器方法的方法對象的SQL腳本參數(shù)名數(shù)組
* @return 存放參數(shù)的object類型數(shù)組對象
*/
private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
//新建一個長度為參數(shù)名數(shù)組長度的Object類型數(shù)組賦值給args
Object[] args = new Object[argumentNames.length];
//遍歷args
for (int i = 0; i < args.length; i++) {
//如果有ProvideMethod有定義ProviderContext類型參數(shù)风范,且 i 等于 ProviderContext類型參數(shù)的所在索引位置
if (providerContextIndex != null && providerContextIndex == i) {
//將providerContext賦值給args[i]
args[i] = providerContext;
//如果ProvideMethod沒有定義ProviderContext類型參數(shù)
} else {
//獲取參數(shù)名對應(yīng)參數(shù)對象咨跌,賦值給args[i]
args[i] = params.get(argumentNames[i]);
}
}
return args;
}
/**
* 執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法,并傳入 {@code args}硼婿, 得到Sql腳本锌半,返回返回出去
* @param args 參數(shù)對象數(shù)組
* @return Sql腳本字符串
*/
private String invokeProviderMethod(Object... args) throws Exception {
Object targetObject = null;
//如果providerMethod不是靜態(tài)方法
if (!Modifier.isStatic(providerMethod.getModifiers())) {
//新建一個providerType的實例對象賦值給targetObject
targetObject = providerType.newInstance();
}
//直接targetObject的providerMethod方法,并傳入?yún)?shù)寇漫,然后得到SQL腳本字符串賦值給sql
CharSequence sql = (CharSequence) providerMethod.invoke(targetObject, args);
//如果sql不為null刊殉,返回sql;否則返回null
return sql != null ? sql.toString() : null;
}
}
RawSqlSource
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.defaults;
import java.util.HashMap;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.session.Configuration;
/**
* Static SqlSource. It is faster than {@link DynamicSqlSource} because mappings are
* calculated during startup.
* <p>
* 調(diào)用 SqlSourceBuilder類將"#{xxx}“ 替換為占位符”?"殉摔,并綁定ParameterMapping,
* 最后返回的RawSqlSource中持有一個由SqlSourceBuilder構(gòu)建的SqlSource對象记焊。
* </p>
* @since 3.2.0
* @author Eduardo Macarron
*/
public class RawSqlSource implements SqlSource {
/**
* 實際為StaticSqlSource
*/
private final SqlSource sqlSource;
/**
*
* @param configuration mybatis全局配置信息
* @param rootSqlNode DML標簽下的子節(jié)點逸月,封裝成MixedSqlNode
* @param parameterType 參數(shù)類型
*/
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
/**
*
* @param configuration mybatis全局配置信息
* @param sql 完整可執(zhí)行SQL
* @param parameterType 參數(shù)類型
*/
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
//解析 SQL 語句,并構(gòu)建 StaticSqlSource遍膜,在此過程中將 sql 語句中的占位符 #{} 替換為問號 ?碗硬,并為每個占位符構(gòu)建相應(yīng)的 ParameterMapping
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
//實際創(chuàng)建的為:StaticSqlSource
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
/**
* 通過遍歷所有的SqlNode,獲取sql
* @param configuration mybatis全局配置信息
* @param rootSqlNode DML標簽下的子節(jié)點捌归,封裝成MixedSqlNode
* @return 完整可執(zhí)行的sql
*/
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
DynamicContext context = new DynamicContext(configuration, null);
rootSqlNode.apply(context);
return context.getSql();
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return sqlSource.getBoundSql(parameterObject);
}
}
StaticSqlSource
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.builder;
import java.util.List;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;
/**
* 靜態(tài)SQL源
* @author Clinton Begin
*/
public class StaticSqlSource implements SqlSource {
/**
* 可執(zhí)行的完整SQL
*/
private final String sql;
/**
* {@link #sql} 的參數(shù)映射集合
*/
private final List<ParameterMapping> parameterMappings;
/**
* mybatis全局配置信息
*/
private final Configuration configuration;
/**
*
* @param configuration mybatis配置信息
* @param sql 可執(zhí)行的完整SQL
*/
public StaticSqlSource(Configuration configuration, String sql) {
this(configuration, sql, null);
}
/**
*
* @param configuration mybatis配置信息
* @param sql 可執(zhí)行的完整SQL
* @param parameterMappings mybatis全局配置信息
*/
public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.configuration = configuration;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
}
BoundSql
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.mapping;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
/**
* An actual SQL String got from an {@link SqlSource} after having processed any dynamic content.
* The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings
* with the additional information for each parameter (at least the property name of the input object to read
* the value from).
* <p>
* Can also have additional parameters that are created by the dynamic language (for loops, bind...).
* <p>
* 參數(shù)映射與可執(zhí)行SQL封裝類對象
* </p>
* <p>
* 其中包含sql語句(該sql語句中可能包含 ? 這樣的占位符), 以及一組parameter mapping(ParameterMapping類的實例),
* 注意這組parameter mapping是Mybatis內(nèi)部生成的(通過讀取#{xx}中的內(nèi)容)
* </p>
* <p>
* 再強調(diào)一次,以上的ParameterMapping實例是在
* ParameterHandler接口的唯一默認實現(xiàn)類 DefaultParameterHandler 中被消費的.
* </p>
* <p>
* BoundSql更像一個中轉(zhuǎn)站, Mybatis在執(zhí)行一次CRUD操作過程中產(chǎn)生的中間數(shù)據(jù)的集中點.
* 這一點觀察其內(nèi)部的字段就可以了解.
* 內(nèi)部基本沒做什么處理, 只是將相應(yīng)的操作調(diào)度給了內(nèi)部的字段
* </p>
* <p>
* <a herf='https://blog.csdn.net/lqzkcx3/article/details/78370497'>https://blog.csdn.net/lqzkcx3/article/details/78370497</a>
* </p>
* @author Clinton Begin
*/
public class BoundSql {
/**
* 進行 #{ } 和 ${ } 替換完畢之后的結(jié)果sql, 注意每個 #{ }替換完之后就是一個 ?
*/
private final String sql;
/**
* 這里的parameterMappings列表參數(shù)里的item個數(shù), 以及每個item的屬性名稱等等, 都是和上面的sql中的 ? 完全一一對應(yīng)的.
*/
private final List<ParameterMapping> parameterMappings;
/**
* 用戶傳入的參數(shù)對象
*/
private final Object parameterObject;
/**
* 額外的參數(shù)
*/
private final Map<String, Object> additionalParameters;
/**
* 元對象參數(shù)肛响,{@link #additionalParameters} 的元對象
*/
private final MetaObject metaParameters;
/**
*
* @param configuration mybatis全局配置信息
* @param sql 進行 #{ } 和 ${ } 替換完畢之后的結(jié)果sql, 注意每個 #{ }替換完之后就是一個 ?
* @param parameterMappings 參數(shù)映射
* @param parameterObject 參數(shù)對象
*/
public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap<>();
this.metaParameters = configuration.newMetaObject(additionalParameters);
}
/**
*
* @return {@see #sql}
*/
public String getSql() {
return sql;
}
/**
*
* @return {@see #parameterMappings}
*/
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
/**
*
* @return {@see #parameterObject}
*/
public Object getParameterObject() {
return parameterObject;
}
/**
* 是否存在額外的參數(shù)
* @param name 參數(shù) 如'order[0].item[0].name'
* @return 通過 {@link PropertyTokenizer} 解析 {@code name} ,再以 {@link PropertyTokenizer#getName()}
* 作為參數(shù)名,判斷是否存在在 {@link #additionalParameters} 中
*/
public boolean hasAdditionalParameter(String name) {
String paramName = new PropertyTokenizer(name).getName();
return additionalParameters.containsKey(paramName);
}
/**
* 對額外的參數(shù)設(shè)置參數(shù)
* @param name 參數(shù)屬性名
* @param value 參數(shù)值
*/
public void setAdditionalParameter(String name, Object value) {
metaParameters.setValue(name, value);
}
/**
* 獲取額外的參數(shù)的對應(yīng) {@code name} 的值
* @param name 參數(shù)名
* @return 對應(yīng) {@code name} 的值
*/
public Object getAdditionalParameter(String name) {
return metaParameters.getValue(name);
}
}