Mybatis3.5.1源码分析
- Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
- Mybatis-Configuration源码解析
- Mybatis-事务对象源码解析
- Mybatis-数据源源码解析
- Mybatis缓存策略源码解析
- Mybatis-DatabaseIdProvider源码解析
- Mybatis-TypeHandler源码解析
- Mybatis-Reflector源码解析
- Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
- Mybatis-Mapper各类标签封装类源码解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
- Mybatis-MapperAnnotationBuilder源码分析
- [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.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脚本的注解类型
*
* 里面就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
*
*/
private static final Set> SQL_ANNOTATION_TYPES = new HashSet<>();
/**
* SQL Provider 注解类型
*
* 里面就是 {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
*
*
* SQL Provider注解用法:https://www.cnblogs.com/he-px/p/7134524.html
*
*/
private static final Set> 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();
//获取结果中所有定义的方法
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) {
//当出现未完成元素时,添加构建Method时抛出异常的MethodResolver实例,到下个Mapper的解析时再次尝试解析
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
//解析未完成解析的Method
parsePendingMethods();
}
/**
* 解析未完成解析的Method
*/
private void parsePendingMethods() {
//获取未完成解析的Method集合
Collection incompleteMethods = configuration.getIncompleteMethods();
//同步加锁,防止多线程情况下,重复解析Method
synchronized (incompleteMethods) {
//获取未解析的Method集合迭代器
Iterator 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
//还是出现未完成解析异常的MethodResolver,留到再下一个Mapper重新解析
}
}
}
}
/**
* 加载对应接口的映射文件
*
* 平时有些项目会将映射文件XML与接口类放在同一个包下,这个方法就是作用于这种方式去加载映射文件XML
*
*/
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的标签
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
//如果有配置CacheNamespace注解
if (cacheDomain != null) {
//获取最大缓存数
Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
//获取刷新时间
Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
// 将 cacheDomain.properties() 转换成Properties
Properties props = convertToProperties(cacheDomain.properties());
//构建一个Cache对象,并保存到Mybatis全局配置信息中
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
}
}
/**
* 将 {@code properties} 转换成Properties
* @param properties 属性数组
* @return Properties对象
*/
private Properties convertToProperties(Property[] properties) {
//如果属性数组为空
if (properties.length == 0) {
//返回null
return null;
}
//新建一个Properties对象,用于保存properties的元素
Properties props = new Properties();
//遍历properties
for (Property property : properties) {
/**
* 先将property.value的有${...}形式的字符串转换成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
*
* - 如果有配置Results注解,拼装resultMapName=接口包名+类名+'.'+id属性值
* - 如果方法有参数 resultMapName=接口包名+类名+'.'+方法名+参数类型拼装字符串
* - 如果方法无参数resultMapName=接口包名+类名+'.'+方法名+'-void'
*
*/
String resultMapId = generateResultMapName(method);
//构建ResultMap实例,包含discriminator的ResultMap实例,并添加到Mybatis全局配置信息
applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
return resultMapId;
}
/**
* 生成 {@code method} 的ResultMapName
* @param method 方法对象
* @return
*
* - 如果有配置Results注解,拼装resultMapName=接口包名+类名+'.'+id属性值
* - 如果方法有参数 resultMapName=接口包名+类名+'.'+方法名+参数类型拼装字符串
* - 如果方法无参数resultMapName=接口包名+类名+'.'+方法名+'-void'
*
*/
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();
//遍历方法的参数类型数组
for (Class> c : method.getParameterTypes()) {
//添加'-'到suffix
suffix.append("-");
//添加参数类型的类名到suffix
suffix.append(c.getSimpleName());
}
//如果suffix为空字符串
if (suffix.length() < 1) {
//添加'-void'到suffix
suffix.append("-void");
}
//拼装resultMapName ,
// 如果方法有参数 resultMapName=接口包名+类名+'.'+方法名+参数类型拼装字符串
// 否则 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类型集合,用于存放结果映射
List resultMappings = new ArrayList<>();
//应用构造函数参数,将 args 的每个元素封装成ResultMapping,添加到 resultMapping 中
applyConstructorArgs(args, returnType, resultMappings);
//应用Result注解数组,将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类型集合,用于存放结果映射
List resultMappings = new ArrayList<>();
// issue #136
//应用构造函数参数,将 c.constructArgs() 的每个元素封装成ResultMapping,添加到 resultMapping 中
applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
//应用Result注解数组,将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注解数组
Case[] cases = discriminator.cases();
//定义鉴别器映射,用于存储Case注解的value,和resultMapId + "-" + value
Map discriminatorMap = new HashMap<>();
//遍历Case注解数组
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) {
/**
* 获取参数类型,如果 {@code method} 的出现多个参数(不包含 RowBounds和ResultHandler)就返回ParamMap类型;
* 如果只有一个参数(不包含 RowBounds和ResultHandler)就返回这一个参数类型
*/
Class> parameterTypeClass = getParameterType(method);
/**
* 获取语言驱动,由先获取{@code method}配置的Lang注解指定的驱动类,如果没有,使用默认的语言驱动类,
* 默认的语言驱动为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注解配置的填入将会被更新的参数对象的属性的值
keyProperty = selectKey.keyProperty();
//如果没有配置options注解
} else if (options == null) {
//useGeneratedKeys:允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)
//Jdbc3KeyGenerator:主要用于数据库的自增主键,比如 MySQL、PostgreSQL;会将执行SQL后从Statemenet中获取主键放到参数对象对应的属性里
//NoKeyGenerator: 什么事情都不干,里面是空实现方法
//如果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注解配置的填入将会被更新的参数对象的属性的值
keyProperty = options.keyProperty();
//获取options注解配置的匹配属性的返回结果集中的列名称
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:这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和
// 这个设置值相等。默认值为未设置(unset)(依赖驱动)。
// 先判断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:这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。
// 默认值为未设置(unset)(依赖驱动)
// 先判断options.timeout的取值范围是否大于-1,是就应用options.timeout;否则为null
timeout = options.timeout() > -1 ? options.timeout() : null;
/**
* SQL脚本类型,默认是 StatementType.PREPARED
*
* - STATEMENT:对应于Statement对象,有SQL注入的风险
* - PREPARED:PreparedStatement,预编译处理
* - CALLABLE:CallableStatement一般调用存储过程的时候使用
*
*/
statementType = options.statementType();
/**
* ResultSet的常量,默认是DEFAULT
*
* - DEFAULT:依赖驱动
* - FORWARD_ONLY:结果集的游标只能向下滚动
* - SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变
* - SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变
*
*/
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);
}
}
/**
* 获取语言驱动,由先获取{@code method}配置的Lang注解指定的驱动类,如果没有,使用默认的语言驱动类,
* 默认的语言驱动为XMLLanguageDriver
* @param method 访对象
* @return {@code method}配置的Lang注解指定的驱动类,如果没有,使用默认的语言驱动类,默认的语言驱动为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全局配置信息中获取语言驱动,当langClass为null时,会使用默认的语言驱动,默认的语言驱动为XMLLanguageDriver
return configuration.getLanguageDriver(langClass);
}
/**
* 获取参数类型,如果 {@code method} 的出现多个参数(不包含 RowBounds和ResultHandler)就返回ParamMap类型;
* 如果只有一个参数(不包含 RowBounds和ResultHandler)就返回这一个参数类型
* @param method 方法对象
* @return 如果 {@code method} 的出现多个参数(不包含 RowBounds和ResultHandler)就返回ParamMap类型;
* 如果只有一个参数(不包含 RowBounds和ResultHandler)就返回这一个参数类型
*/
private Class> getParameterType(Method method) {
Class> parameterType = null;
//获取参数类型数组
Class>[] parameterTypes = method.getParameterTypes();
//遍历参数类型
for (Class> currentParameterType : parameterTypes) {
//如果currentParameterType不是RowBound的子类或是本身,又不是ResultHandler的子类或者本身
if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
//如果参数类型为null
if (parameterType == null) {
//将currentParameterType赋值给parameterType
parameterType = currentParameterType;
//参数类型不为null时,表示出现多个参数
} else {
// issue #135 使用ParamMap作为参数类型
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) {
//强转resolvedReturnType为Class类型并赋值给returnType
returnType = (Class>) resolvedReturnType;
//如果returnType是数组
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注解配置的结果对象类型赋值给returnType
returnType = rt.value();
}
}
//ParameterizedType : 参数化类型,参考博客:https://blog.csdn.net/JustBeauty/article/details/81116144
//如果resolvedResturnType 是 ParameteizedType的子类
} else if (resolvedReturnType instanceof ParameterizedType) {
//将resolvedReturnType强转成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的参数化类型参数数组
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强转为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强转为ParameterizedType类型赋值给returnType
returnType = (Class>) ((ParameterizedType) returnTypeParameter).getRawType();
//GenericArrayType是Type的子接口,用于表示“泛型数组”,描述的是形如:A[]
// 或T[]的类型。其实也就是描述ParameterizedType类型以及TypeVariable类型的数组
// ,即形如:classA[][]、T[]等。
//如果returnTypeParameter是GenericArrayType的子类或是其本身
} else if (returnTypeParameter instanceof GenericArrayType) {
//将returnTypeParameter强转为GenericArrayType类型赋值给componentType
Class> componentType = (Class>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
// (gcode issue #525) support List
// 新建一个长度为0的componentType类型数组对象并获取该数组对象的类赋值给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的参数化类型参数数组
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强转为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强转为ParameterizedType类型赋值给returnType
returnType = (Class>) ((ParameterizedType) returnTypeParameter).getRawType();
}
}
//Optional:主要解决的问题是臭名昭著的空指针异常(NullPointerException)
//如果rawType为Optional类
} else if (Optional.class.equals(rawType)) {
//获取parameterizedType的参数化类型参数数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
//获取参数化类型参数数组的第一个元素
Type returnTypeParameter = actualTypeArguments[0];
//如果returnTypeParameter是Class的子类或是其本身
if (returnTypeParameter instanceof Class>) {
//将returnTypeParameter强转为Class类型赋值给returnType
returnType = (Class>) returnTypeParameter;
}
}
}
return returnType;
}
/**
* 从 {@code method} 的注解中构建SQL源对象
* @param method 方法对象
* @param parameterType 参数类型
* @param languageDriver 语言驱动
* @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脚本字符串数组
* @param parameterTypeClass 参数类型
* @param languageDriver 语言驱动
* @return SQL源
*/
private SqlSource buildSqlSourceFromStrings(String[] strings, Class> parameterTypeClass, LanguageDriver languageDriver) {
//新建一个StringBuilder对象用于将strings转换成字符串
final StringBuilder sql = new StringBuilder();
//遍历strings
for (String fragment : strings) {
//添加fragemet到sql
sql.append(fragment);
//添加空格到sql
sql.append(" ");
}
//通过语音驱动构建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> types) {
//遍历注解列表
for (Class extends Annotation> type : types) {
//从method中获取type注解对象
Annotation annotation = method.getAnnotation(type);
//如果method中有配置type注解
if (annotation != null) {
//直接返回注解
return type;
}
}
return null;
}
/**
* 应用Result注解数组,将 {@code results} 的每个元素封装成ResultMapping,添加到 {@code resultMapping}中
* @param results Result注解数组
* @param resultType 返回类型
* @param resultMappings 结果映射集合
*/
private void applyResults(Result[] results, Class> resultType, List resultMappings) {
//遍历Result注解数组
for (Result result : results) {
//ResultFlag枚举类型:ResultFlag.ID-表示是个表主键;ResultFlag.CONSTRUCTOR-表示是个构造函数参数
//定义存放ResultFlag的集合
List 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;
}
/**
* 应用构造函数参数,将 {@code args} 的每个元素封装成ResultMapping,添加到 {@code resultMapping}中
* @param args Arg注解数组
* @param resultType 返回类型
* @param resultMappings 结果映射集合
*/
private void applyConstructorArgs(Arg[] args, Class> resultType, List resultMappings) {
//遍历Arg注解数组
for (Arg arg : args) {
//ResultFlag枚举类型:ResultFlag.ID-表示是个表主键;ResultFlag.CONSTRUCTOR-表示是个构造函数参数
//定义存放ResultFlag的集合
List flags = new ArrayList<>();
//添加构建函数标记给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 参数类型
* @param languageDriver 语言驱动
* @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注解配置的填入将会被更新的参数对象的属性的值
String keyProperty = selectKeyAnnotation.keyProperty();
//获取selectKey注解配置的 匹配属性的返回结果集中的列名称
String keyColumn = selectKeyAnnotation.keyColumn();
//获取selectKey注解配置的 SQL 语句应被在插入语句的之前还是之后执行标记
boolean executeBefore = selectKeyAnnotation.before();
// defaults 定义默认值
//定义不使用缓存
boolean useCache = false;
//定义KeyGenerator为NoKeyGenerator.NoKeyGenerator:什么事情都不干,里面是空实现方法
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
//fetchSize:这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)
Integer fetchSize = null;
//timeout:这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
Integer timeout = null;
//flushCache:将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
boolean flushCache = false;
//参数映射
String parameterMap = null;
//结果映射
String resultMap = null;
/**
* FORWARD_ONLY:结果集的游标只能向下滚动,
* SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变,
* SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变
* DEFAULT:依赖驱动(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。
*/
ResultSetType resultSetTypeEnum = null;
/**
* 将 selectKey注解的会被执行的 SQL 字符串数组 拼装成完整的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);
// 根据id 获取对应的MappedStatement对象
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
// 用于执行selectKey标签的SQL,将结果赋值到参数对象对应的属性中
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
//添加 KeyGenerator对象到Mybatis全局配置信息
configuration.addKeyGenerator(id, answer);
return answer;
}
}