Mybatis3.5.1源碼分析
- Mybatis-SqlSessionFactoryBuilder姑原,XMLConfigBuilder,XPathParser源碼解析
- Mybatis-Configuration源碼解析
- Mybatis-事務對象源碼解析
- 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源碼解析
MapperAnnotationBuilder
/**
* 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.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.CacheNamespaceRef;
import org.apache.ibatis.annotations.Case;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Lang;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Options.FlushCachePolicy;
import org.apache.ibatis.annotations.Property;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.TypeDiscriminator;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.CacheRefResolver;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Discriminator;
import org.apache.ibatis.mapping.FetchType;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;
/**
* 映射器注解構建器
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class MapperAnnotationBuilder {
/**
* SQL腳本的注解類型
* <p>
* 里面就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
* </p>
*/
private static final Set<Class<? extends Annotation>> SQL_ANNOTATION_TYPES = new HashSet<>();
/**
* SQL Provider 注解類型
* <p>
* 里面就是 {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
* </p>
* <p>
* SQL Provider注解用法:<a >https://www.cnblogs.com/he-px/p/7134524.html</a>
* </p>
*/
private static final Set<Class<? extends Annotation>> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>();
/**
* mybatis配置類
*/
private final Configuration configuration;
/**
* 映射構建器助理
*/
private final MapperBuilderAssistant assistant;
/**
* 當前接口類
*/
private final Class<?> type;
static {
SQL_ANNOTATION_TYPES.add(Select.class);
SQL_ANNOTATION_TYPES.add(Insert.class);
SQL_ANNOTATION_TYPES.add(Update.class);
SQL_ANNOTATION_TYPES.add(Delete.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(SelectProvider.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(InsertProvider.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(UpdateProvider.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(DeleteProvider.class);
}
/**
*
* @param configuration Mybatis全局配置信息
* @param type 映射接口類
*/
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
//假設傳進來的type.getName='bin.study.mapper.IUser',那么resource='bin/study/mapper/IUser.java (best guess)'
String resource = type.getName().replace('.', '/') + ".java (best guess)";
//新建一個映射構建器助理
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
}
/**
* 解析映射接口類扔罪,解析mapper標簽下的所有方法和注解始腾,并對解析出來的信息加以封裝,
* 然后添加到Mybatis全局配置信息中呕寝。然后重新解析Mybatis全局配置信息中未能完成解析的
* Method重新解析
*/
public void parse() {
String resource = type.toString();//假設是 type='接口類IUser',resource就是'interface bin.study.mapper.IUser'
//如果resource沒有被加載過
if (!configuration.isResourceLoaded(resource)) {
//加載對應接口的映射文件
loadXmlResource();
//將resource添加到已加載的資源集合中勋眯,以防止重新加載resource
configuration.addLoadedResource(resource);
//設置構建器助理的當前命名空間為type的包+類名
assistant.setCurrentNamespace(type.getName());
//解析CacheNamespace注解,構建一個Cache對象下梢,并保存到Mybatis全局配置信息中
parseCache();
//解析CacheNamespace注解客蹋,引用CacheRef對應的Cache對象
parseCacheRef();
//獲取結(jié)果中所有定義的方法
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237 如果method不是橋接方法,什么是橋接方法:https://www.cnblogs.com/zsg88/p/7588929.html
if (!method.isBridge()) {
//構建MapperStatement對象孽江,并添加到Mybatis全局配置信息中
parseStatement(method);
}
} catch (IncompleteElementException e) {
//當出現(xiàn)未完成元素時讶坯,添加構建Method時拋出異常的MethodResolver實例,到下個Mapper的解析時再次嘗試解析
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
//解析未完成解析的Method
parsePendingMethods();
}
/**
* 解析未完成解析的Method
*/
private void parsePendingMethods() {
//獲取未完成解析的Method集合
Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
//同步加鎖岗屏,防止多線程情況下辆琅,重復解析Method
synchronized (incompleteMethods) {
//獲取未解析的Method集合迭代器
Iterator<MethodResolver> iter = incompleteMethods.iterator();
//遍歷未解析的Method集合
while (iter.hasNext()) {
try {
//取出MethodResolver對象,重新解析这刷,構建MapperStatement對象婉烟,并添加到Mybatis全局配置信息中
iter.next().resolve();
//移除解析成功的MethodResolver對象
iter.remove();
} catch (IncompleteElementException e) {
// This method is still missing a resource
//還是出現(xiàn)未完成解析異常的MethodResolver,留到再下一個Mapper重新解析
}
}
}
}
/**
* 加載對應接口的映射文件
* <p>
* 平時有些項目會將映射文件XML與接口類放在同一個包下暇屋,這個方法就是作用于這種方式去加載映射文件XML
* </p>
*/
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
/**
* 因為Spring框架可能不知道真正的資源名似袁,所以我們檢查一個標記去防止加載兩次資源。
* 這個標記設置在{@link XMLMapperBuilder#bindMapperForNamespace()}
*/
//判斷是否加載過映射文件XML
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
//假設type='interface bin.study.mapper.IUser',那么xmlResource='bin/study/mapper/IUser.xml'
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
//嘗試加載映射文件
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
//如果文件流為null
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
//搜索不在這模塊里但是在這個類路徑下的xml map
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required 忽略異常咐刨,因為這里的資源不是必須的
}
}
//如果文件流不null
if (inputStream != null) {
//新建一個XML映射構建器
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
/**
* 解析Mapper.xml昙衅,解析mapper標簽下的所有標簽,并對解析出來的標簽信息加以封裝定鸟,
* 然后添加到Mybatis全局配置信息中而涉。然后重新解析Mybatis全局配置信息中未能完成解析的
* ResultMap標簽信息,CacheRef標簽信息仔粥,DML標簽信息
*/
xmlParser.parse();
}
}
}
/**
* 解析CacheNamespace注解婴谱,構建一個Cache對象蟹但,并保存到Mybatis全局配置信息中
*/
private void parseCache() {
//CacheNamespace 相當于Mapper.xml的<cache>標簽
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
//如果有配置CacheNamespace注解
if (cacheDomain != null) {
//獲取最大緩存數(shù)
Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
//獲取刷新時間
Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
// 將 cacheDomain.properties() 轉(zhuǎn)換成Properties
Properties props = convertToProperties(cacheDomain.properties());
//構建一個Cache對象,并保存到Mybatis全局配置信息中
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
}
}
/**
* 將 {@code properties} 轉(zhuǎn)換成Properties
* @param properties 屬性數(shù)組
* @return Properties對象
*/
private Properties convertToProperties(Property[] properties) {
//如果屬性數(shù)組為空
if (properties.length == 0) {
//返回null
return null;
}
//新建一個Properties對象谭羔,用于保存properties的元素
Properties props = new Properties();
//遍歷properties
for (Property property : properties) {
/**
* 先將property.value的有${...}形式的字符串轉(zhuǎn)換成Mybatis全局配置信息維護變量表的對應字符串,
* eg: '${first_name},${initial},${last_name}' => 'James,T,Kirk'
* 再添加到props中
*/
props.setProperty(property.name(),
PropertyParser.parse(property.value(), configuration.getVariables()));
}
return props;
}
/**
* 解析CacheNamespace注解华糖,引用CacheRef對應的Cache對象
*/
private void parseCacheRef() {
//CacheNamespaceRef相當于Mapper.xml的CacheRef標簽
CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
//如果有配置CacheNamespace注解
if (cacheDomainRef != null) {
//獲取Mapper接口類
Class<?> refType = cacheDomainRef.value();
//獲取Mapper.xml命名空間
String refName = cacheDomainRef.name();
//如果沒有配置refType又沒有配置refName
if (refType == void.class && refName.isEmpty()) {
//拋出異常
throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
}
//如果有配置refType又配置了refName
if (refType != void.class && !refName.isEmpty()) {
//拋出異常
throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
}
//如果配置了refType就用refType,否則使用refName
String namespace = (refType != void.class) ? refType.getName() : refName;
try {
/**
* CacheRef對應的Cache對象瘟裸,會從Mybatis全局配信息中獲取 {@code namespace} 對應的Cache對象客叉,
* 如果沒有找到,會拋出IncompleteElementException異常话告,找到會將Cache對象賦值給
* currentCache兼搏,在構建MapperStatement對象時,將currentCache傳進去
*/
assistant.useCacheRef(namespace);
//捕捉未完成元素構建成封裝類異常
} catch (IncompleteElementException e) {
//新建一個CacheRefResolver保存namespace和構建器助理對象沙郭,待解析構建下一個Mapper時再次嘗試構建
configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace));
}
}
}
/**
* 構建ResultMap實例,包含discriminator的ResultMap實例佛呻,并添加到Mybatis全局配置信息,
* 然后返回ResultMapId
* @param method 方法對象
* @return ResultMapId
*/
private String parseResultMap(Method method) {
//獲取 method 的返回類型
Class<?> returnType = getReturnType(method);
//獲取method配置的ConstructorArgs注解
ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
//獲取method配置的Results注解
Results results = method.getAnnotation(Results.class);
//獲取method配置的TypeDiscirminator注解
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
/**
* 生成method的resultMapId
* <ol>
* <li>如果有配置Results注解,拼裝resultMapName=接口包名+類名+'.'+id屬性值</li>
* <li>如果方法有參數(shù) resultMapName=接口包名+類名+'.'+方法名+參數(shù)類型拼裝字符串</li>
* <li>如果方法無參數(shù)resultMapName=接口包名+類名+'.'+方法名+'-void'</li>
* </ol>
*/
String resultMapId = generateResultMapName(method);
//構建ResultMap實例,包含discriminator的ResultMap實例病线,并添加到Mybatis全局配置信息
applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
return resultMapId;
}
/**
* 生成 {@code method} 的ResultMapName
* @param method 方法對象
* @return
* <ol>
* <li>如果有配置Results注解吓著,拼裝resultMapName=接口包名+類名+'.'+id屬性值</li>
* <li>如果方法有參數(shù) resultMapName=接口包名+類名+'.'+方法名+參數(shù)類型拼裝字符串</li>
* <li>如果方法無參數(shù)resultMapName=接口包名+類名+'.'+方法名+'-void'</li>
* </ol>
*/
private String generateResultMapName(Method method) {
//獲取method的Results注解
Results results = method.getAnnotation(Results.class);
//如果有配置results注解 且 results注解id不為空
if (results != null && !results.id().isEmpty()) {
//拼裝resultMapName ,resultMapName=接口包名+類名+'.'+id屬性值
return type.getName() + "." + results.id();
}
//定義ResultMapName的后綴
StringBuilder suffix = new StringBuilder();
//遍歷方法的參數(shù)類型數(shù)組
for (Class<?> c : method.getParameterTypes()) {
//添加'-'到suffix
suffix.append("-");
//添加參數(shù)類型的類名到suffix
suffix.append(c.getSimpleName());
}
//如果suffix為空字符串
if (suffix.length() < 1) {
//添加'-void'到suffix
suffix.append("-void");
}
//拼裝resultMapName ,
// 如果方法有參數(shù) resultMapName=接口包名+類名+'.'+方法名+參數(shù)類型拼裝字符串
// 否則 resultMapName=接口包名+類名+'.'+方法名+'-void'
return type.getName() + "." + method.getName() + suffix;
}
/**
* 構建ResultMap實例,包含discriminator的ResultMap實例,并添加到Mybatis全局配置信息
* @param resultMapId resultMapId
* @param returnType 返回類型
* @param args Arg注解集合送挑,相當于arg標簽绑莺,和idArg標簽
* @param results result注解集合,相當于Result標簽
* @param discriminator TypeDiscriminator注解惕耕,相當于discriminator標簽
*/
private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
//定義一個ResultMapping類型集合纺裁,用于存放結(jié)果映射
List<ResultMapping> resultMappings = new ArrayList<>();
//應用構造函數(shù)參數(shù),將 args 的每個元素封裝成ResultMapping司澎,添加到 resultMapping 中
applyConstructorArgs(args, returnType, resultMappings);
//應用Result注解數(shù)組欺缘,將results的每個元素封裝成ResultMapping,添加到resultMapping中
applyResults(results, returnType, resultMappings);
//構建discriminator對象
Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
// TODO add AutoMappingBehaviour
//構建ResultMap實例惭缰,并添加到Mybatis全局配置信息
assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
//將鑒別器的Case配置信息封裝成ResultMap,并添加到Mybatis全局配置信息
createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
}
/**
* 將鑒別器的Case配置信息封裝成ResultMap,并添加到Mybatis全局配置信息
* @param resultMapId resultMapId
* @param resultType 返回類型
* @param discriminator TypeDiscriminator注解
*/
private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
//如果方法有配置TypeDiscriminator注解
if (discriminator != null) {
//遍歷TypeDiscriminator注解的cases屬性
for (Case c : discriminator.cases()) {
//拼裝caseResultMapId
String caseResultMapId = resultMapId + "-" + c.value();
//定義一個ResultMapping類型集合浪南,用于存放結(jié)果映射
List<ResultMapping> resultMappings = new ArrayList<>();
// issue #136
//應用構造函數(shù)參數(shù)笼才,將 c.constructArgs() 的每個元素封裝成ResultMapping漱受,添加到 resultMapping 中
applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
//應用Result注解數(shù)組,將c.results()的每個元素封裝成ResultMapping骡送,添加到resultMapping中
applyResults(c.results(), resultType, resultMappings);
// TODO add AutoMappingBehaviour
//構建ResultMap實例昂羡,并添加到Mybatis全局配置信息
assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
}
}
}
/**
* 構建discriminator對象
* @param resultMapId resultMapId
* @param resultType 返回類型
* @param discriminator TypeDiscriminator注解
* @return discriminator對象
*/
private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
//如果方法有配置TypeDiscriminator注解
if (discriminator != null) {
//獲取TypeDiscriminator注解的列名
String column = discriminator.column();
//如果沒有配置TypeDiscriminator注解的javaType,javaType就為String.class摔踱;否則javaType就是TypeDiscriminator注解的javaType
Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
//如果沒有配置TypeDiscriminator注解的jdbcType虐先,jdbcType就為null;否則javaType就是TypeDiscriminator注解的jdbcType
JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
//如果discriminator的typeHandler屬性值為UnknowTypeHandler,typeHandler就為null派敷;
// 否則typeHandler就是discriminator的typeHandler屬性值
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
(discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler());
//獲取TypeDiscriminator注解的Case注解數(shù)組
Case[] cases = discriminator.cases();
//定義鑒別器映射蛹批,用于存儲Case注解的value,和resultMapId + "-" + value
Map<String, String> discriminatorMap = new HashMap<>();
//遍歷Case注解數(shù)組
for (Case c : cases) {
//獲取Case注解的value屬性值
String value = c.value();
//拼裝caseResultMapId
String caseResultMapId = resultMapId + "-" + value;
//將Case注解的value屬性值和caseResultMapId添加到discriminatorMap中
discriminatorMap.put(value, caseResultMapId);
}
//構建鑒別器對象
return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
}
return null;
}
/**
* 構建MapperStatement對象撰洗,并添加到Mybatis全局配置信息中
* @param method 方法對象
*/
void parseStatement(Method method) {
/**
* 獲取參數(shù)類型,如果 {@code method} 的出現(xiàn)多個參數(shù)(不包含 RowBounds和ResultHandler)就返回ParamMap類型腐芍;
* 如果只有一個參數(shù)(不包含 RowBounds和ResultHandler)就返回這一個參數(shù)類型
*/
Class<?> parameterTypeClass = getParameterType(method);
/**
* 獲取語言驅(qū)動差导,由先獲取{@code method}配置的Lang注解指定的驅(qū)動類,如果沒有猪勇,使用默認的語言驅(qū)動類,
* 默認的語言驅(qū)動為XMLLanguageDriver
*/
LanguageDriver languageDriver = getLanguageDriver(method);
//從 method 的注解中構建SQL源對象
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
//如果SQL源對象不為null
if (sqlSource != null) {
//獲取method中的Options注解對象
Options options = method.getAnnotation(Options.class);
//拼裝mappedStatementId
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = null;
//獲取當前 method 的SqlCommandType枚舉對象
SqlCommandType sqlCommandType = getSqlCommandType(method);
//是否是查詢指令
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//如果是查詢指令,刷新緩存為false,否則為true
boolean flushCache = !isSelect;
//如果是查詢指令渐裸,使用緩存為true;否則為false
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = null;
String keyColumn = null;
//如果sqlCommandType為插入指令類型 或者 sqlCommandType為修改指令類型
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
// 譯文:先檢查SelectKey注解命浴,它將覆蓋其他的一切
//獲取method配置的SelectKey注解對象
SelectKey selectKey = method.getAnnotation(SelectKey.class);
//如果selectKey不為null
if (selectKey != null) {
//構建 {@code selectKeyAnnotation} 對應的KeyGenerator對象,
//并將KeyGenerator對象添加到Mybatis全局配置信息中
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
//獲取seleckKey注解配置的填入將會被更新的參數(shù)對象的屬性的值
keyProperty = selectKey.keyProperty();
//如果沒有配置options注解
} else if (options == null) {
//useGeneratedKeys:允許 JDBC 支持自動生成主鍵椅您,需要驅(qū)動支持外冀。 如果設置為 true 則這個設置強制使用自動生成主鍵,盡管一些驅(qū)動不能支持但仍可正常工作(比如 Derby)
//Jdbc3KeyGenerator:主要用于數(shù)據(jù)庫的自增主鍵掀泳,比如 MySQL锥惋、PostgreSQL;會將執(zhí)行SQL后從Statemenet中獲取主鍵放到參數(shù)對象對應的屬性里
//NoKeyGenerator: 什么事情都不干开伏,里面是空實現(xiàn)方法
//如果isUseGeneratedKeys為true膀跌,就使用Jdbc3KeyGenerator;否則使用NoKeyGenerator
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
//如果selectKey為null 且 有配置options注解
} else {
//如果options注解中的userGeneratedKey屬性為true,就使用Jdbc3KeyGenerator;
// 否則使用NoKeyGenerator
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
//獲取options注解配置的填入將會被更新的參數(shù)對象的屬性的值
keyProperty = options.keyProperty();
//獲取options注解配置的匹配屬性的返回結(jié)果集中的列名稱
keyColumn = options.keyColumn();
}
//如果sqlCommandType為select指令類型固灵,或者delete指令類型
} else {
//因為select和delete指令不會應該有KeyGenerator的功能捅伤,所以使用NoKeyGenerator
keyGenerator = NoKeyGenerator.INSTANCE;
}
//如果有配置options注解
if (options != null) {
//FlushCachePolicy.TRUE 表示 不管是什么類型指令都刷新緩存
//如果options注解的flushCache為FlushCachePolicy.TRUE
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
//flushCach設置為true
flushCache = true;
//FlushCachePolicy.FALSE 表示 不管是什么類型指令都不刷新緩存
//如果options注解的flushCache為FlushCachePolicy.FALSE
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
//flushCach設置為false
flushCache = false;
}
//options.useCache: 是否使用緩存,默認為true
useCache = options.useCache();
//options.fetchSize:這是一個給驅(qū)動的提示巫玻,嘗試讓驅(qū)動程序每次批量返回的結(jié)果行數(shù)和
// 這個設置值相等丛忆。默認值為未設置(unset)(依賴驅(qū)動)。
// 先判斷options.fetchSize取值范圍是否大于-1仍秤,或者等于Integer.MIN_VALUE熄诡。
// 是就是引用options.fetchSize;否則為null
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
//options.timeout:這個設置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)诗力。
// 默認值為未設置(unset)(依賴驅(qū)動)
// 先判斷options.timeout的取值范圍是否大于-1凰浮,是就應用options.timeout;否則為null
timeout = options.timeout() > -1 ? options.timeout() : null;
/**
* SQL腳本類型苇本,默認是 StatementType.PREPARED
* <ol>
* <li>STATEMENT:對應于Statement對象袜茧,有SQL注入的風險</li>
* <li>PREPARED:PreparedStatement,預編譯處理</li>
* <li>CALLABLE:CallableStatement一般調(diào)用存儲過程的時候使用</li>
* </ol>
*/
statementType = options.statementType();
/**
* ResultSet的常量,默認是DEFAULT
* <ol>
* <li>DEFAULT:依賴驅(qū)動</li>
* <li>FORWARD_ONLY:結(jié)果集的游標只能向下滾動</li>
* <li>SCROLL_INSENSITIVE:結(jié)果集的游標可以上下移動瓣窄,當數(shù)據(jù)庫變化時笛厦,當前結(jié)果集不變</li>
* <li>SCROLL_SENSITIVE:返回可滾動的結(jié)果集,當數(shù)據(jù)庫變化時俺夕,當前結(jié)果集同步改變</li>
* </ol>
*/
resultSetType = options.resultSetType();
}
String resultMapId = null;
//獲取method的ResultMap注解
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
//如果有配置resultMap注解
if (resultMapAnnotation != null) {
//拼裝resultMap注解的配置的resultMapId,使用','進行拼接
resultMapId = String.join(",", resultMapAnnotation.value());
//如果是查詢指令
} else if (isSelect) {
//構建ResultMap實例,包含discriminator的ResultMap實例裳凸,并添加到Mybatis全局配置信息,
//然后返回ResultMapId
resultMapId = parseResultMap(method);
}
//構建MapperStatement對象贱鄙,并添加到Mybatis全局配置信息中
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
/**
* 獲取語言驅(qū)動,由先獲取{@code method}配置的Lang注解指定的驅(qū)動類姨谷,如果沒有贰逾,使用默認的語言驅(qū)動類,
* 默認的語言驅(qū)動為XMLLanguageDriver
* @param method 訪對象
* @return {@code method}配置的Lang注解指定的驅(qū)動類菠秒,如果沒有疙剑,使用默認的語言驅(qū)動類,默認的語言驅(qū)動為XMLLanguageDriver
*/
private LanguageDriver getLanguageDriver(Method method) {
//獲取method配置的Lang注解
Lang lang = method.getAnnotation(Lang.class);
Class<? extends LanguageDriver> langClass = null;
//如果method有加上Lang注解
if (lang != null) {
//獲取Lang注解配置的value賦值給langClass
langClass = lang.value();
}
//從Mybatis全局配置信息中獲取語言驅(qū)動践叠,當langClass為null時言缤,會使用默認的語言驅(qū)動,默認的語言驅(qū)動為XMLLanguageDriver
return configuration.getLanguageDriver(langClass);
}
/**
* 獲取參數(shù)類型禁灼,如果 {@code method} 的出現(xiàn)多個參數(shù)(不包含 RowBounds和ResultHandler)就返回ParamMap類型管挟;
* 如果只有一個參數(shù)(不包含 RowBounds和ResultHandler)就返回這一個參數(shù)類型
* @param method 方法對象
* @return 如果 {@code method} 的出現(xiàn)多個參數(shù)(不包含 RowBounds和ResultHandler)就返回ParamMap類型;
* 如果只有一個參數(shù)(不包含 RowBounds和ResultHandler)就返回這一個參數(shù)類型
*/
private Class<?> getParameterType(Method method) {
Class<?> parameterType = null;
//獲取參數(shù)類型數(shù)組
Class<?>[] parameterTypes = method.getParameterTypes();
//遍歷參數(shù)類型
for (Class<?> currentParameterType : parameterTypes) {
//如果currentParameterType不是RowBound的子類或是本身弄捕,又不是ResultHandler的子類或者本身
if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
//如果參數(shù)類型為null
if (parameterType == null) {
//將currentParameterType賦值給parameterType
parameterType = currentParameterType;
//參數(shù)類型不為null時僻孝,表示出現(xiàn)多個參數(shù)
} else {
// issue #135 使用ParamMap作為參數(shù)類型
parameterType = ParamMap.class;
}
}
}
return parameterType;
}
/**
* 獲取 {@code method} 的返回類型
* @param method 方法對象
* @return {@code method} 的返回類型
*/
private Class<?> getReturnType(Method method) {
//獲取method的返回類型
Class<?> returnType = method.getReturnType();
//解析獲取method在type中的返回類型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
//如果reolvedReturnType是Class的子類
if (resolvedReturnType instanceof Class) {
//強轉(zhuǎn)resolvedReturnType為Class類型并賦值給returnType
returnType = (Class<?>) resolvedReturnType;
//如果returnType是數(shù)組
if (returnType.isArray()) {
//獲取returnType的元素類型再次賦值給returnType
returnType = returnType.getComponentType();
}
// gcode issue #508 如果returnType為void類
if (void.class.equals(returnType)) {
//獲取method配置的ResultType注解
ResultType rt = method.getAnnotation(ResultType.class);
//如果有配置resultType注解
if (rt != null) {
//將resultType注解配置的結(jié)果對象類型賦值給returnType
returnType = rt.value();
}
}
//ParameterizedType : 參數(shù)化類型,參考博客:https://blog.csdn.net/JustBeauty/article/details/81116144
//如果resolvedResturnType 是 ParameteizedType的子類
} else if (resolvedReturnType instanceof ParameterizedType) {
//將resolvedReturnType強轉(zhuǎn)成ParameterizedType類型守谓,然后賦值給parameterizedType
ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
// 獲取parameterizedType的原始類型并賦值給rawType
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
//如果rawType為Collection的子類或是其本身 有或者是Cursor的子類或是其本身
if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
//獲取parameterizedType的參數(shù)化類型參數(shù)數(shù)組
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
//如果actualTypeArguments不為null且actualTypeArguments只有一個元素
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
//獲取actualTypeArguments的第一個元素并賦值給returnTypeParameter
Type returnTypeParameter = actualTypeArguments[0];
//如果returnTypeParameter是Class類的子類或是其本身
if (returnTypeParameter instanceof Class<?>) {
//將returnTypeParameter強轉(zhuǎn)為Class類型然后賦值給returnType
returnType = (Class<?>) returnTypeParameter;
//如果returnTypeParameter是ParaeterizedType的子類或是其本身
} else if (returnTypeParameter instanceof ParameterizedType) {
// (gcode issue #443) actual type can be a also a parameterized type
//將returnTypeParameter強轉(zhuǎn)為ParameterizedType類型賦值給returnType
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
//GenericArrayType是Type的子接口穿铆,用于表示“泛型數(shù)組”,描述的是形如:A<T>[]
// 或T[]的類型斋荞。其實也就是描述ParameterizedType類型以及TypeVariable類型的數(shù)組
// 荞雏,即形如:classA<T>[][]、T[]等平酿。
//如果returnTypeParameter是GenericArrayType的子類或是其本身
} else if (returnTypeParameter instanceof GenericArrayType) {
//將returnTypeParameter強轉(zhuǎn)為GenericArrayType類型賦值給componentType
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
// (gcode issue #525) support List<byte[]>
// 新建一個長度為0的componentType類型數(shù)組對象并獲取該數(shù)組對象的類賦值給returnType
returnType = Array.newInstance(componentType, 0).getClass();
}
}
//如果method有配置MapKey注解 且 rawType是Map的子類或是其本身
} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
// (gcode issue 504) Do not look into Maps if there is not MapKey annotation
//獲取parameterizedType的參數(shù)化類型參數(shù)數(shù)組
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
//如果actualTypeArguments不為null且actualTypeArguments只有兩個元素
if (actualTypeArguments != null && actualTypeArguments.length == 2) {
//獲取Map的value類型
Type returnTypeParameter = actualTypeArguments[1];
//如果returnTypeParameter是Class的子類或是其本身
if (returnTypeParameter instanceof Class<?>) {
//將returnTypeParameter強轉(zhuǎn)為Class類型賦值給returnType
returnType = (Class<?>) returnTypeParameter;
//如果returnTypeParameter是ParameterizedType的子類或是其本身
} else if (returnTypeParameter instanceof ParameterizedType) {
// (gcode issue 443) actual type can be a also a parameterized type
//將returnTypeParameter強轉(zhuǎn)為ParameterizedType類型賦值給returnType
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
}
}
//Optional:主要解決的問題是臭名昭著的空指針異常(NullPointerException)
//如果rawType為Optional類
} else if (Optional.class.equals(rawType)) {
//獲取parameterizedType的參數(shù)化類型參數(shù)數(shù)組
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
//獲取參數(shù)化類型參數(shù)數(shù)組的第一個元素
Type returnTypeParameter = actualTypeArguments[0];
//如果returnTypeParameter是Class的子類或是其本身
if (returnTypeParameter instanceof Class<?>) {
//將returnTypeParameter強轉(zhuǎn)為Class類型賦值給returnType
returnType = (Class<?>) returnTypeParameter;
}
}
}
return returnType;
}
/**
* 從 {@code method} 的注解中構建SQL源對象
* @param method 方法對象
* @param parameterType 參數(shù)類型
* @param languageDriver 語言驅(qū)動
* @return ProviderSqlSource對象,ProviderSqlSource: SQL提供者的SQL源
*/
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
//找出配置在method中的SQL指令類型注解凤优,就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
/**
* 找出配置在 method中的SQL Provider 注解,就是
* {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
*/
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
//如果有配置SQL指令類型
if (sqlAnnotationType != null) {
//如果有配置SQL provide注解
if (sqlProviderAnnotationType != null) {
//拋出異常蜈彼,不可以同時配置SQL指令類型注解和SQL Provider 注解
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
}
//獲取sqlAnnotionType注解對象
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
//同過反射的方式取出SQL指令類型注解的value方法的返回值
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
//將strings拼裝成完整的SQL腳本字符串筑辨,然后通過languageDriver構建SQL源
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
} else if (sqlProviderAnnotationType != null) {
//獲取sqlProviderAnnotationType注解對象
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
//新建一個SQL提供者的SQL源
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
}
return null;
} catch (Exception e) {
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
}
}
/**
* 將 {@code strings} 拼裝成完整的SQL腳本字符串,然后通過 {@code languageDriver} 構建SQL源
* @param strings sql腳本字符串數(shù)組
* @param parameterTypeClass 參數(shù)類型
* @param languageDriver 語言驅(qū)動
* @return SQL源
*/
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
//新建一個StringBuilder對象用于將strings轉(zhuǎn)換成字符串
final StringBuilder sql = new StringBuilder();
//遍歷strings
for (String fragment : strings) {
//添加fragemet到sql
sql.append(fragment);
//添加空格到sql
sql.append(" ");
}
//通過語音驅(qū)動構建SQL源
return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
}
/**
* 獲取 {@code method} 的SqlCommandType枚舉對象
* @param method 方法對象
* @return {@code method} 的SQL指令類型
*/
private SqlCommandType getSqlCommandType(Method method) {
/**
* 找出配置在 {@code method} 中的SQL指令類型注解幸逆,
* 就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
*/
Class<? extends Annotation> type = getSqlAnnotationType(method);
//如果沒有配置SQL指令類型
if (type == null) {
/**
* 找出配置在 {@code method} 中的SQL Provider 注解類型棍辕,就是
* {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
*/
type = getSqlProviderAnnotationType(method);
//如果沒有配置SQL provider注解類型
if (type == null) {
//直接返回 未知的SQL指令類型
return SqlCommandType.UNKNOWN;
}
//如果類型等于SelectProvider注解
if (type == SelectProvider.class) {
//類型就是為Select注解
type = Select.class;
//如果類型等于InsertProvider注解
} else if (type == InsertProvider.class) {
//類型就是為Insert注解
type = Insert.class;
//如果類型等于UpdateProvider注解
} else if (type == UpdateProvider.class) {
//類型就是Update注解
type = Update.class;
//如果類型等于DeleteProvider注解
} else if (type == DeleteProvider.class) {
//類型就是Delete注解
type = Delete.class;
}
}
//對應type對應的SqlCommandType枚舉對象
return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
}
/**
* 找出配置在 {@code method} 中的SQL指令類型,就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
* @param method 方法對象
* @return SQL指令類型秉颗,就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
*/
private Class<? extends Annotation> getSqlAnnotationType(Method method) {
//找出 method 中第一個對應 SQL_ANNOTATION_TYPES 的元素的注解
return chooseAnnotationType(method, SQL_ANNOTATION_TYPES);
}
/**
* 找出配置在 {@code method} 中的SQL Provider 注解類型痢毒,就是
* {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
* @param method 方法對象
* @return SQL Provider 注解類型送矩,就是
* {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
*/
private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
////找出 method 中第一個對應 SQL_PROVIDER_ANNOTATION_TYPES 的元素的注解
return chooseAnnotationType(method, SQL_PROVIDER_ANNOTATION_TYPES);
}
/**
* 找出 {@code method} 中第一個對應 {@code types}的元素的注解
* @param method 方法對象
* @param types 注解集合
* @return {@code method} 中第一個對應 {@code types}的元素的注解
*/
private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
//遍歷注解列表
for (Class<? extends Annotation> type : types) {
//從method中獲取type注解對象
Annotation annotation = method.getAnnotation(type);
//如果method中有配置type注解
if (annotation != null) {
//直接返回注解
return type;
}
}
return null;
}
/**
* 應用Result注解數(shù)組蚕甥,將 {@code results} 的每個元素封裝成ResultMapping,添加到 {@code resultMapping}中
* @param results Result注解數(shù)組
* @param resultType 返回類型
* @param resultMappings 結(jié)果映射集合
*/
private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
//遍歷Result注解數(shù)組
for (Result result : results) {
//ResultFlag枚舉類型:ResultFlag.ID-表示是個表主鍵;ResultFlag.CONSTRUCTOR-表示是個構造函數(shù)參數(shù)
//定義存放ResultFlag的集合
List<ResultFlag> flags = new ArrayList<>();
//如果result的id屬性為true
if (result.id()) {
//添加主鍵標記給flags
flags.add(ResultFlag.ID);
}
//如果result的typeHandler屬性值為UnknowTypeHandler,typeHandler就為null栋荸;
// 否則typeHandler就是result的typeHandler屬性值
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler());
//對Result注解配置信息封裝ResultMapping對象
ResultMapping resultMapping = assistant.buildResultMapping(
resultType,
nullOrEmpty(result.property()),
nullOrEmpty(result.column()),
result.javaType() == void.class ? null : result.javaType(),
result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
hasNestedSelect(result) ? nestedSelectId(result) : null,
null,
null,
null,
typeHandler,
flags,
null,
null,
isLazy(result));
//將resultMapping添加到resultMapping集合中
resultMappings.add(resultMapping);
}
}
private String nestedSelectId(Result result) {
String nestedSelect = result.one().select();
if (nestedSelect.length() < 1) {
nestedSelect = result.many().select();
}
if (!nestedSelect.contains(".")) {
nestedSelect = type.getName() + "." + nestedSelect;
}
return nestedSelect;
}
private boolean isLazy(Result result) {
boolean isLazy = configuration.isLazyLoadingEnabled();
if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
isLazy = result.one().fetchType() == FetchType.LAZY;
} else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
isLazy = result.many().fetchType() == FetchType.LAZY;
}
return isLazy;
}
private boolean hasNestedSelect(Result result) {
if (result.one().select().length() > 0 && result.many().select().length() > 0) {
throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
}
return result.one().select().length() > 0 || result.many().select().length() > 0;
}
/**
* 應用構造函數(shù)參數(shù)菇怀,將 {@code args} 的每個元素封裝成ResultMapping凭舶,添加到 {@code resultMapping}中
* @param args Arg注解數(shù)組
* @param resultType 返回類型
* @param resultMappings 結(jié)果映射集合
*/
private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
//遍歷Arg注解數(shù)組
for (Arg arg : args) {
//ResultFlag枚舉類型:ResultFlag.ID-表示是個表主鍵;ResultFlag.CONSTRUCTOR-表示是個構造函數(shù)參數(shù)
//定義存放ResultFlag的集合
List<ResultFlag> flags = new ArrayList<>();
//添加構建函數(shù)標記給flags
flags.add(ResultFlag.CONSTRUCTOR);
//如果arg的id屬性為true
if (arg.id()) {
//添加主鍵標記給flags
flags.add(ResultFlag.ID);
}
//如果arg的typeHandler屬性值為UnknowTypeHandler,typeHandler就為null;
// 否則typeHandler就是arg的typeHandler屬性值
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
(arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler());
//arg注解配置信息封裝ResultMapping對象
ResultMapping resultMapping = assistant.buildResultMapping(
resultType,
nullOrEmpty(arg.name()),
nullOrEmpty(arg.column()),
arg.javaType() == void.class ? null : arg.javaType(),
arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
nullOrEmpty(arg.select()),
nullOrEmpty(arg.resultMap()),
null,
nullOrEmpty(arg.columnPrefix()),
typeHandler,
flags,
null,
null,
false);
//將resultMapping添加到resultMapping集合中
resultMappings.add(resultMapping);
}
}
/**
* 如果 {@code value} 為null或者為空字符串爱沟,都會返回null;否則返回 {@code value}
* @param value 值
*/
private String nullOrEmpty(String value) {
return value == null || value.trim().length() == 0 ? null : value;
}
private Result[] resultsIf(Results results) {
return results == null ? new Result[0] : results.value();
}
private Arg[] argsIf(ConstructorArgs args) {
return args == null ? new Arg[0] : args.value();
}
/**
* 構建 {@code selectKeyAnnotation} 對應的KeyGenerator對象帅霜,并將KeyGenerator對象添加到
* Mybatis全局配置信息中
* @param selectKeyAnnotation SelectKey注解對象
* @param baseStatementId MappedStatement對象Id
* @param parameterTypeClass 參數(shù)類型
* @param languageDriver 語言驅(qū)動
* @return {@code selectKeyAnnotation} 對應的KeyGenerator對象
*/
private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
//拼裝SelectKeyId,SelectKeyId=MappedStatement對象Id+'!selectKey'
String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
//獲取selectKey注解配置的keyPropety屬性的java類型
Class<?> resultTypeClass = selectKeyAnnotation.resultType();
//獲取selectKey注解配置的SQL腳本類型
StatementType statementType = selectKeyAnnotation.statementType();
//獲取seleckKey注解配置的填入將會被更新的參數(shù)對象的屬性的值
String keyProperty = selectKeyAnnotation.keyProperty();
//獲取selectKey注解配置的 匹配屬性的返回結(jié)果集中的列名稱
String keyColumn = selectKeyAnnotation.keyColumn();
//獲取selectKey注解配置的 SQL 語句應被在插入語句的之前還是之后執(zhí)行標記
boolean executeBefore = selectKeyAnnotation.before();
// defaults 定義默認值
//定義不使用緩存
boolean useCache = false;
//定義KeyGenerator為NoKeyGenerator.NoKeyGenerator:什么事情都不干呼伸,里面是空實現(xiàn)方法
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
//fetchSize:這是一個給驅(qū)動的提示身冀,嘗試讓驅(qū)動程序每次批量返回的結(jié)果行數(shù)和這個設置值相等。 默認值為未設置(unset)(依賴驅(qū)動)
Integer fetchSize = null;
//timeout:這個設置是在拋出異常之前括享,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)搂根。默認值為未設置(unset)(依賴驅(qū)動)。
Integer timeout = null;
//flushCache:將其設置為 true 后铃辖,只要語句被調(diào)用剩愧,都會導致本地緩存和二級緩存被清空,默認值:false娇斩。
boolean flushCache = false;
//參數(shù)映射
String parameterMap = null;
//結(jié)果映射
String resultMap = null;
/**
* FORWARD_ONLY:結(jié)果集的游標只能向下滾動仁卷,
* SCROLL_SENSITIVE:返回可滾動的結(jié)果集,當數(shù)據(jù)庫變化時犬第,當前結(jié)果集同步改變,
* SCROLL_INSENSITIVE:結(jié)果集的游標可以上下移動锦积,當數(shù)據(jù)庫變化時,當前結(jié)果集不變
* DEFAULT:依賴驅(qū)動(等價于 unset) 中的一個歉嗓,默認值為 unset (依賴驅(qū)動)充包。
*/
ResultSetType resultSetTypeEnum = null;
/**
* 將 selectKey注解的會被執(zhí)行的 SQL 字符串數(shù)組 拼裝成完整的SQL腳本字符串,
* 然后通過languageDriver 構建SQL源
*/
SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
//設置sql指令類型為SELECT
SqlCommandType sqlCommandType = SqlCommandType.SELECT;
//構建MapperStatement對象遥椿,并添加到全局配置信息中
assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
flushCache, useCache, false,
keyGenerator, keyProperty, keyColumn, null, languageDriver, null);
//檢查ID是否簡寫基矮,簡寫就應用當前命名空間+id
id = assistant.applyCurrentNamespace(id, false);
// 根據(jù)id 獲取對應的MappedStatement對象
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
// 用于執(zhí)行selectKey標簽的SQL,將結(jié)果賦值到參數(shù)對象對應的屬性中
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
//添加 KeyGenerator對象到Mybatis全局配置信息
configuration.addKeyGenerator(id, answer);
return answer;
}
}