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源码解析
ParameterMap
/**
* Copyright 2009-2015 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.Collections;
import java.util.List;
import org.apache.ibatis.session.Configuration;
/**
* 参数映射信息封装类,Mapper.xml的parameterMap标签
* @author Clinton Begin
*/
public class ParameterMap {
/**
* ParameterMap标签的ID属性
*/
private String id;
/**
* ParamterMap标签的type属性,即参数类型
*/
private Class> type;
/**
* ParamterMap标签的所有paramter标签,即参数的所有属性列表
*/
private List parameterMappings;
private ParameterMap() {
}
/**
* ParameterMap的构造器
*/
public static class Builder {
private ParameterMap parameterMap = new ParameterMap();
/**
*
* @param configuration 这个参数已经不用了,很有可能后面的版本去掉
* @param id ParameterMap标签的ID属性
* @param type ParamterMap标签的type属性,即参数类型
* @param parameterMappings ParamterMap标签的所有paramter标签,即参数的所有属性列表
*/
public Builder(Configuration configuration, String id, Class> type, List parameterMappings) {
parameterMap.id = id;
parameterMap.type = type;
parameterMap.parameterMappings = parameterMappings;
}
/**
* 返回参数类型
*/
public Class> type() {
return parameterMap.type;
}
public ParameterMap build() {
//lock down collections 锁定集合
/**
* Collections.unmodifiableList方法会将传进来的集合变成只读的集合,
* 只要改变该集合都会抛出UnsupportedOperationException。
* 总之,可以保证对象的list内容不被意料之外地修改,保证对象的封装性
*/
parameterMap.parameterMappings = Collections.unmodifiableList(parameterMap.parameterMappings);
return parameterMap;
}
}
public String getId() {
return id;
}
public Class> getType() {
return type;
}
public List getParameterMappings() {
return parameterMappings;
}
}
ParameterMapping
/**
* 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.sql.ResultSet;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* 参数映射
*
* 对应于Mapper.xml的:
* <parameter property="" resultMap="" javaType="" typeHandler="" jdbcType="" mode="" scale=""></parameter>
*
*
* 参考博客:https://blog.csdn.net/L_Sail/article/details/73824674
*
* @author Clinton Begin
*/
public class ParameterMapping {
private Configuration configuration;
/**
* 属性名
*/
private String property;
/**
* mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,
* 参数对象属性的真实值将会被改变,就像你在获取输出参数时所期望的那样。
* 如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),
* 必须指定一个 resultMap 来映射结果集到参数类型
*/
private ParameterMode mode;
/**
* java类型
*/
private Class> javaType = Object.class;
/**
* jdbc类型
*/
private JdbcType jdbcType;
/**
* 小数点后保留的位数
*/
private Integer numericScale;
/**
* 类型处理器
*/
private TypeHandler> typeHandler;
/**
* 结果映射ID
*/
private String resultMapId;
/**
* jdbc类型名, SQL 结构类型的完全限定名称
*
* 作用于CallableStatement.registerOutParameter (int parameterIndex, int sqlType, String typeName)方法的
* typeName参数:
* 在执行存储过程调用之前,必须显式调用 registerOutParameter 为每个 OUT 参数注册来自 java.sql.Types 的类型。
* 对于用户定义的参数,还应该提供该参数的完全限定 SQL 类型名称,而 REF 参数则要求提供所引用类型的完全限定类型名称。
* 不需要类型代码和类型名称信息的 JDBC 驱动程序可以忽略它。不过,为了便于移植,应用程序应该为用户定义的参数和 REF 参数提供这些值。
*
*/
private String jdbcTypeName;
/**
* 表达式
*/
private String expression;
private ParameterMapping() {
}
public static class Builder {
private ParameterMapping parameterMapping = new ParameterMapping();
/**
*
* @param configuration mybatis的配置信息类
* @param property 属性名
* @param typeHandler 属性类型处理器
*/
public Builder(Configuration configuration, String property, TypeHandler> typeHandler) {
parameterMapping.configuration = configuration;
parameterMapping.property = property;
parameterMapping.typeHandler = typeHandler;
parameterMapping.mode = ParameterMode.IN;
}
/**
*
* @param configuration mybatis的配置信息类
* @param property 属性名
* @param javaType 属性类型
*/
public Builder(Configuration configuration, String property, Class> javaType) {
parameterMapping.configuration = configuration;
parameterMapping.property = property;
parameterMapping.javaType = javaType;
parameterMapping.mode = ParameterMode.IN;
}
/**
* mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像你在获取输出参数时所期望的那样。
* 如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 来映射结果集到参数类型
*/
public Builder mode(ParameterMode mode) {
parameterMapping.mode = mode;
return this;
}
/**
* 属性类型
*/
public Builder javaType(Class> javaType) {
parameterMapping.javaType = javaType;
return this;
}
/**
* 属性的jdbcType
*/
public Builder jdbcType(JdbcType jdbcType) {
parameterMapping.jdbcType = jdbcType;
return this;
}
/**
* 参数的属性的小数点保留的位数
*/
public Builder numericScale(Integer numericScale) {
parameterMapping.numericScale = numericScale;
return this;
}
/**
* 参数的属性对应的结果映射ID
*/
public Builder resultMapId(String resultMapId) {
parameterMapping.resultMapId = resultMapId;
return this;
}
/**
* 参数的属性对应的TypeHandler
*/
public Builder typeHandler(TypeHandler> typeHandler) {
parameterMapping.typeHandler = typeHandler;
return this;
}
/**
* 参数的属性对应的jdbcType名
*/
public Builder jdbcTypeName(String jdbcTypeName) {
parameterMapping.jdbcTypeName = jdbcTypeName;
return this;
}
/**
* 参数的属性正则表达式
*/
public Builder expression(String expression) {
parameterMapping.expression = expression;
return this;
}
/**
* 构建ParameterMapping
*
* 在调用其ParameterMapping.Builder的构造方法时,就已经构建了parameterMapping。
* 而方法时就是返回这个paramterMapping,在返回之前,会检查有没有设置TypeHandler,没有的
* 话,会尝试根据javaType从TypeHandler注册器中获取对应javaType的TypeHandler实例.
* 然后 检查一下业务需求所需要的属性是否缺漏,缺漏的抛出异常。如
*
*
* - 如果是javaType为ResultSet的时候,需要设置resultMapId属性,因为需要根据resultMap来映射获取数据。
* 否则抛出异常
* - 如果是javaType为其他类型是,必须要有对应其类型的typeHandler的实例。否则抛出异常
*
*/
public ParameterMapping build() {
//解析获取TypeHandler实例
resolveTypeHandler();
// 检查一下业务需求所需要的属性是否缺漏,缺漏的抛出异常
validate();
return parameterMapping;
}
/**
* 检查一下业务需求所需要的属性是否缺漏
*
* 如果是javaType为ResultSet的时候,需要设置resultMapId属性,因为需要根据resultMap来映射获取数据。
* 否则抛出异常
*
*
* 如果是javaType为其他类型是,必须要有对应其类型的typeHandler的实例。否则抛出异常
*
*/
private void validate() {
//当jdbcType为JdbcType.CURSOR是,javaType就会是ResultSet
if (ResultSet.class.equals(parameterMapping.javaType)) {
//如果是ResultSet就必须要有resultMapId,因为需要根据resultMap来映射获取数据。
if (parameterMapping.resultMapId == null) {
throw new IllegalStateException("Missing resultmap in property '"
+ parameterMapping.property + "'. "
+ "Parameters of type java.sql.ResultSet require a resultmap.");
}
} else {
//javaType为其他类型时,必须要有对应其类型的typeHandler的实例。
if (parameterMapping.typeHandler == null) {
throw new IllegalStateException("Type handler was null on parameter mapping for property '"
+ parameterMapping.property + "'. It was either not specified and/or could not be found for the javaType ("
+ parameterMapping.javaType.getName() + ") : jdbcType (" + parameterMapping.jdbcType + ") combination.");
}
}
}
/**
* 解析获取TypeHandler实例
*
* 在typeHandler为null且有javaType的情况下才会尝试从TypeHandler注册器获取对应的TypeHandler实例
*
*/
private void resolveTypeHandler() {
if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
Configuration configuration = parameterMapping.configuration;
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
//只是获取typeHandler,没有注册
parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
}
}
}
public String getProperty() {
return property;
}
/**
* Used for handling output of callable statements.
* @return
*/
public ParameterMode getMode() {
return mode;
}
/**
* Used for handling output of callable statements.
* @return
*/
public Class> getJavaType() {
return javaType;
}
/**
* Used in the UnknownTypeHandler in case there is no handler for the property type.
* @return
*/
public JdbcType getJdbcType() {
return jdbcType;
}
/**
* Used for handling output of callable statements.
* @return
*/
public Integer getNumericScale() {
return numericScale;
}
/**
* Used when setting parameters to the PreparedStatement.
* @return
*/
public TypeHandler> getTypeHandler() {
return typeHandler;
}
/**
* Used for handling output of callable statements.
* @return
*/
public String getResultMapId() {
return resultMapId;
}
/**
* Used for handling output of callable statements.
* @return
*/
public String getJdbcTypeName() {
return jdbcTypeName;
}
/**
* Not used
* @return
*/
public String getExpression() {
return expression;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ParameterMapping{");
//sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
sb.append("property='").append(property).append('\'');
sb.append(", mode=").append(mode);
sb.append(", javaType=").append(javaType);
sb.append(", jdbcType=").append(jdbcType);
sb.append(", numericScale=").append(numericScale);
//sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
sb.append(", resultMapId='").append(resultMapId).append('\'');
sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\'');
sb.append(", expression='").append(expression).append('\'');
sb.append('}');
return sb.toString();
}
}
ResultMap
/**
* 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.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ParamNameUtil;
import org.apache.ibatis.session.Configuration;
/**
* 结果映射信息封装类,Mapper.xml的resultMap标签信息封装类对象
* @author Clinton Begin
*/
public class ResultMap {
private Configuration configuration;
/**
* resultMap标签的Id属性
*/
private String id;
/**
* resultMap标签的type属性,resultMap的java类型
*/
private Class> type;
/**
* 结果映射
*/
private List resultMappings;
/**
* id结果映射
*/
private List idResultMappings;
/**
* 构造函数结果映射,
*
* 存放着带有构造函数标记ResultFlag.CONSTRUCTOR的resultMapping
*
*/
private List constructorResultMappings;
/**
* 属性结果映射。
* 将没有构造函数标记ResultFalg.CONSTRUCTOR的resultMapping添加到propertyResultMapping,
* 没有构造函数标记ResultFalg.CONSTRUCTOR的resultMapping其实可以认为就是resultMap标签
* 下的result标签和id标签
*/
private List propertyResultMappings;
/**
* 映射列
*
* {@link #resultMappings}的所有column属性集合,包括嵌套的reusltMapping的column,
* 所保存的列是以大写形式保存
*
*/
private Set mappedColumns;
/**
* 映射属性,存放着所有resultMapping的所有propery
*/
private Set mappedProperties;
/**
* 鉴别器
*/
private Discriminator discriminator;
/**
* 有嵌套映射结果,判断reusltMapping中有没有设置嵌套的 resultMap id
*/
private boolean hasNestedResultMaps;
/**
* 有嵌套查询,判断reusltMapping中有没有设置select属性
*/
private boolean hasNestedQueries;
/**
* 自动映射
*/
private Boolean autoMapping;
private ResultMap() {
}
public static class Builder {
private static final Log log = LogFactory.getLog(Builder.class);
private ResultMap resultMap = new ResultMap();
/**
*
* @param configuration mybatis全局配置
* @param id resultMap标签的ID属性
* @param type resultMap标签的type属性,resultMap的java类型
* @param resultMappings 结果元素映射集合
*/
public Builder(Configuration configuration, String id, Class> type, List resultMappings) {
this(configuration, id, type, resultMappings, null);
}
/**
*
* @param configuration mybatis全局配置
* @param id resultMap标签的ID属性
* @param type resultMap标签的type属性,resultMap的java类型
* @param resultMappings 结果元素映射集合
* @param autoMapping 自动映射
*/
public Builder(Configuration configuration, String id, Class> type, List resultMappings, Boolean autoMapping) {
resultMap.configuration = configuration;
resultMap.id = id;
resultMap.type = type;
resultMap.resultMappings = resultMappings;
resultMap.autoMapping = autoMapping;
}
/**
* 鉴别器
* @param discriminator 鉴别器
* @return
*/
public Builder discriminator(Discriminator discriminator) {
resultMap.discriminator = discriminator;
return this;
}
/**
* 返回resultMap的java类型
* @return {@link #resultMap#type}
*/
public Class> type() {
return resultMap.type;
}
/**
* 构建resultMap
*
*/
public ResultMap build() {
//验证Id,Id是找到这个reusltMap的关键,不可以没有。
if (resultMap.id == null) {
throw new IllegalArgumentException("ResultMaps must have an id");
}
//给定下面属性一个空集合实例
resultMap.mappedColumns = new HashSet<>();
resultMap.mappedProperties = new HashSet<>();
resultMap.idResultMappings = new ArrayList<>();
resultMap.constructorResultMappings = new ArrayList<>();
resultMap.propertyResultMappings = new ArrayList<>();
//存放着带有构造函数标记的ResultMapping的property
final List constructorArgNames = new ArrayList<>();
for (ResultMapping resultMapping : resultMap.resultMappings) {
//有嵌套查询标记更新
resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
//有嵌套ResultMap更新
resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
/**
* 将column添加到ResultMap.mappedColumn列表中,这个reusltMapping是个组合的reusltMapping,
* 也会将组合的reusltMapping的column添加上去
*/
final String column = resultMapping.getColumn();
if (column != null) {
resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
} else if (resultMapping.isCompositeResult()) {
for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
final String compositeColumn = compositeResultMapping.getColumn();
if (compositeColumn != null) {
resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
}
}
}
//将resultMapping的property添加到mappedProperties
final String property = resultMapping.getProperty();
if (property != null) {
resultMap.mappedProperties.add(property);
}
//将带有构造函数标记ResultFlag.CONSTRUCTOR的resultMapping添加到constructorResultMapping
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
resultMap.constructorResultMappings.add(resultMapping);
//添加带有构造函数标记ResultFlag.CONSTRUCTOR的resultMapping的property
if (resultMapping.getProperty() != null) {
constructorArgNames.add(resultMapping.getProperty());
}
} else {
/**
*将没有构造函数标记ResultFalg.CONSTRUCTOR的resultMapping添加到propertyResultMapping,
* 没有构造函数标记ResultFalg.CONSTRUCTOR的resultMapping其实可以认为就是resultMap标签
* 下的result标签和id标签
*/
resultMap.propertyResultMappings.add(resultMapping);
}
//将带有ID标记ResultFlag.ID的resultMapping添加的idResultMapping
if (resultMapping.getFlags().contains(ResultFlag.ID)) {
resultMap.idResultMappings.add(resultMapping);
}
}
//TODO 为啥在没有获取到带有ID标记ResultFlag.ID的resultMapping时就添加所有resultMaping?
if (resultMap.idResultMappings.isEmpty()) {
resultMap.idResultMappings.addAll(resultMap.resultMappings);
}
if (!constructorArgNames.isEmpty()) {
/**
* 获取对应construtorArgNames的构造函数的参数名列表,这里忽略constructorArgNames的元素顺序是否和
* 对应的构造函数的参数顺序对应,只对应他们的类型和参数名
*/
final List actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
/**
* 这个异常触发条件我觉得会很容易触发的。
* 1.在如果在mybatis-config.xml文件中设置了useActualParamName为false的时候,resultMap对应的
* java类型的构造函数的参数又没有加上@param注解时,就会导致和constructorArgNames匹配不上。
*/
if (actualArgNames == null) {
throw new BuilderException("Error in result map '" + resultMap.id
+ "'. Failed to find a constructor in '"
+ resultMap.getType().getName() + "' by arg names " + constructorArgNames
+ ". There might be more info in debug log.");
}
//让constructorResultMapping根据构造函数的参数名列表actualArgNames进行排序。
resultMap.constructorResultMappings.sort((o1, o2) -> {
int paramIdx1 = actualArgNames.indexOf(o1.getProperty());
int paramIdx2 = actualArgNames.indexOf(o2.getProperty());
return paramIdx1 - paramIdx2;
});
}
// lock down collections
resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
return resultMap;
}
/**
* 匹配构造函数的参数名称
* @param constructorArgNames 带有构造函数标记的ResultMapping的property集合
* @return 返回已经匹配验证成功的构造函数的参数名称,在匹配验证不成功的时候会返回null
*/
private List argNamesOfMatchingConstructor(List constructorArgNames) {
Constructor>[] constructors = resultMap.type.getDeclaredConstructors();
for (Constructor> constructor : constructors) {
Class>[] paramTypes = constructor.getParameterTypes();
if (constructorArgNames.size() == paramTypes.length) {
List paramNames = getArgNames(constructor);
/**
* paramNames有可能返回args0,args1...,这个时候containsAll返回false,使得整个方法返回null。
* 这里argTypesMatch方法并不会接收到paramNames为args0,args1...的情况,因为这里这里使用'&&',
* 在containAll返回false的时候,是不会执行argTypesMatch方法。
*/
if (constructorArgNames.containsAll(paramNames)
&& argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
return paramNames;
}
}
}
return null;
}
/**
* 匹配构造函数的参数类型
* @param constructorArgNames 带有构造函数标记的ResultMapping的property集合
* @param paramTypes 构造函数的所有参数的类型
* @param paramNames 构造函数的所有参数的参数名
* @return 在 {@link #resultMap#constructorResultMappings} 的所有javaType与
* paramTypes取得的类型在类型上都相等的情况下就会返回true
*/
private boolean argTypesMatch(final List constructorArgNames,
Class>[] paramTypes, List paramNames) {
for (int i = 0; i < constructorArgNames.size(); i++) {
/**
* 从constructorArgNames获取i位置的property,再从paramNames获取property对应的位置,
* 再从paramTypes取得该位置对应的类型
*/
Class> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
//从构造函数ResultMapping中获取i位置的ResultMapping的javaType
Class> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
/**
* 这里判断不对应的情况下是不会抛出异常,只会打印log,并返回false,之所以没有抛出异常时是因为
* 统一交给了build方法抛出异常,这里只是简单的调用Class#equal方法,Class并没有对
* equal方法进行重写,所以equal方法相当于'=='的判断结果,也就是说这里没有考虑到父子类的情况。
*
* 这里不存在因为顺序不一致而导致不相等的情况,因为actualType的通过constructorArgNames的位置获取的,
* 而constructorArgNames和resultMap.constructorResultMappings同一个循环遍历下填充元素的,所以
* constructorArgNames和resultMap.constructorResultMappings的元素位置是一致的。
*/
if (!actualType.equals(specifiedType)) {
if (log.isDebugEnabled()) {
log.debug("While building result map '" + resultMap.id
+ "', found a constructor with arg names " + constructorArgNames
+ ", but the type of '" + constructorArgNames.get(i)
+ "' did not match. Specified: [" + specifiedType.getName() + "] Declared: ["
+ actualType.getName() + "]");
}
return false;
}
}
return true;
}
/**
* 获取构造函数的参数名
* @param constructor {@link #type} 的构造函数
* @return
*
* - 尝试获取 {@code constructor} 的参数的 {@link Param} 作为参数名。
* - 在开启了 {@link #configuration#useActualParamName} 情况下,尝试获取 {@code constructor} 的参数名作为参数名
* - 最后方案:用"arg" + 参数索引作为参数名
*
*/
private List getArgNames(Constructor> constructor) {
List paramNames = new ArrayList<>();
List actualParamNames = null;
//获取constructor每个参数的注解
final Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
int paramCount = paramAnnotations.length;
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
String name = null;
//找到@Param注解并把其value赋给name
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
name = ((Param) annotation).value();
break;
}
}
//在开启了configuration#useActualParamName情况下,会使用在构造函数的参数名赋值给name
if (name == null && resultMap.configuration.isUseActualParamName()) {
if (actualParamNames == null) {
actualParamNames = ParamNameUtil.getParamNames(constructor);
}
//要保证构造函数的参数名数量是大于参数索引paramIndex,否则在获取参数名时会抛出异常
if (actualParamNames.size() > paramIndex) {
name = actualParamNames.get(paramIndex);
}
}
/**
* 1,在仍然获取不到name的情况下,会以'arg'+paramIndex 作为name
* 2.添加到paramNames
*/
paramNames.add(name != null ? name : "arg" + paramIndex);
}
return paramNames;
}
}
public String getId() {
return id;
}
public boolean hasNestedResultMaps() {
return hasNestedResultMaps;
}
public boolean hasNestedQueries() {
return hasNestedQueries;
}
public Class> getType() {
return type;
}
public List getResultMappings() {
return resultMappings;
}
public List getConstructorResultMappings() {
return constructorResultMappings;
}
public List getPropertyResultMappings() {
return propertyResultMappings;
}
public List getIdResultMappings() {
return idResultMappings;
}
public Set getMappedColumns() {
return mappedColumns;
}
public Set getMappedProperties() {
return mappedProperties;
}
public Discriminator getDiscriminator() {
return discriminator;
}
public void forceNestedResultMaps() {
hasNestedResultMaps = true;
}
public Boolean getAutoMapping() {
return autoMapping;
}
}
Discriminator
/**
* Copyright 2009-2015 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.Collections;
import java.util.Map;
import org.apache.ibatis.session.Configuration;
/**
* 鉴别器
* 用于对一个查询可能出现的不同结果集做鉴别,根据具体的条件为 resultMap 动态选择返回值类型。
*
* eg:
* <discriminator javaType="int" column="type">
* <case value="1" resultType="Apple"/>
* <case value="2" resultType="Banana"/>
* <discriminator/>
*
*
*
* 参考博客:https://blog.csdn.net/weixin_36210698/article/details/83508113
*
* @author Clinton Begin
*/
public class Discriminator {
/**
* ResutMap标签的属性标签封装
*/
private ResultMapping resultMapping;
/**
* case标签信息封装,映射
*
* key=case标签的value属性,value=命名空间+case标签的resultMap属性(reusltMapId),
*
*/
private Map discriminatorMap;
Discriminator() {
}
public static class Builder {
private Discriminator discriminator = new Discriminator();
public Builder(Configuration configuration, ResultMapping resultMapping, Map discriminatorMap) {
discriminator.resultMapping = resultMapping;
discriminator.discriminatorMap = discriminatorMap;
}
public Discriminator build() {
assert discriminator.resultMapping != null;
assert discriminator.discriminatorMap != null;
assert !discriminator.discriminatorMap.isEmpty();
//lock down map
discriminator.discriminatorMap = Collections.unmodifiableMap(discriminator.discriminatorMap);
return discriminator;
}
}
public ResultMapping getResultMapping() {
return resultMapping;
}
public Map getDiscriminatorMap() {
return discriminatorMap;
}
public String getMapIdFor(String s) {
return discriminatorMap.get(s);
}
}
MappedStatement
/**
* 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.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.cache.Cache;
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.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
/**
*
* Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
*
*
* MappedStatement类在Mybatis框架中用于表示XML文件中一个sql语句节点,
* 即一个、或者标签。Mybatis框架在初始化阶段会对XML配置文件进行读取
* ,将其中的sql语句节点对象化为一个个MappedStatement对象。
*
* @author Clinton Begin
*/
public final class MappedStatement {
/**
* 资源路径,当前项目,有可能是java文件也有可能是.xml文件
*/
private String resource;
/**
* mybatis全局配置信息
*/
private Configuration configuration;
/**
* 节点中的id属性加要命名空间
*/
private String id;
/**
* select标签的fetchSize属性
*
*
* 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
*
*
*
* 假设fetchSize为5,查询总记录数为100,每批5次返回给你,最后结果还是总记录数,只是提高点查询的速度而已
*
*
*
* MySQL不支持fetchSize,默认为一次性取出所有数据。所以容易导致OOM,
* 如果是Oracle的话就是默认取出fetchSize条数据。
* 裸露JDBC防止OOM可以调用statement的enableStreamingResults方法,
* MyBatis应该在<select fetchSize="-2147483648">。
*
*/
private Integer fetchSize;
/**
* 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。
* 默认值为 unset(依赖驱动)。
*/
private Integer timeout;
/**
*
* - STATEMENT:对应于Statement对象,有SQL注入的风险
* - PREPARED:PreparedStatement,预编译处理
* - CALLABLE:CallableStatement一般调用存储过程的时候使用
*
* 默认是PREPARED
*/
private StatementType statementType;
/**
* 其实的就是{@link java.sql.ResultSet}的常量,只不过Mybatis对其常量进行封装成枚举。
*
* - DEFAULT:依赖驱动
* - FORWARD_ONLY:结果集的游标只能向下滚动
* - SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变
* - SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变
*
*/
private ResultSetType resultSetType;
/**
* 构建动态SQL
*/
private SqlSource sqlSource;
/**
* 每条语句都对就一个缓存,如果有的话
*/
private Cache cache;
/**
* 参数映射信息封装类,Mapper.xml的parameterMap标签
*/
private ParameterMap parameterMap;
/**
* 结果映射信息封装类,Mapper.xml的resultMap标签 集合
*/
private List resultMaps;
/**
* 需要刷新缓存标记
*
* 如果配置了flushCacheRequired为true,则会在执行器执行之前就清空本地一级缓存,换句话说就是关闭一级缓存功能
*
*/
private boolean flushCacheRequired;
/**
* 启用缓存标记
*/
private boolean useCache;
/**
* select标签
*
* 如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,
* 就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。
* 默认值:false。
*
*
* 参考博客:https://blog.csdn.net/isea533/article/details/51533296?utm_source=blogxgwz9
*
*/
private boolean resultOrdered;
/**
* SQL指令类型
*
* - UNKNOWN:未知
* - INSERT:插入
* - UPDATE:修改
* - DELETE:删除
* - SELECT:查询
* - FLUSH:刷新
*
*/
private SqlCommandType sqlCommandType;
/**
* Key生成器
*/
private KeyGenerator keyGenerator;
/**
* (仅对 insert 和 update 有用)唯一标记一个属性,
* MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,
* 默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
*/
private String[] keyProperties;
/**
* (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,
* 这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。
* 如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表
*/
private String[] keyColumns;
/**
* 有嵌套映射结果标记,这个标记除了ParamterMap里面的resultMap外还保护ParamterMap里面的resultMap的resultMap
*/
private boolean hasNestedResultMaps;
/**
* DML标签中配置的databaseId属性
*/
private String databaseId;
/**
* 日志对象
*/
private Log statementLog;
/**
* 语言驱动,默认是 {@link org.apache.ibatis.scripting.xmltags.XMLLanguageDriver}
*/
private LanguageDriver lang;
/**
* 结果集
*/
private String[] resultSets;
MappedStatement() {
// constructor disabled
}
public static class Builder {
/**
* 默认对象
*/
private MappedStatement mappedStatement = new MappedStatement();
/**
*
* @param configuration mybatis全局配置信息
* @param id 节点中的id属性加要命名空间
* @param sqlSource 构建动态SQL
* @param sqlCommandType SQL指令类型
*/
public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
mappedStatement.configuration = configuration;
mappedStatement.id = id;
mappedStatement.sqlSource = sqlSource;
mappedStatement.statementType = StatementType.PREPARED;
mappedStatement.resultSetType = ResultSetType.DEFAULT;
mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<>()).build();
mappedStatement.resultMaps = new ArrayList<>();
mappedStatement.sqlCommandType = sqlCommandType;
mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
String logId = id;
//加上全局日志的前缀
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
mappedStatement.statementLog = LogFactory.getLog(logId);
mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
}
public Builder resource(String resource) {
mappedStatement.resource = resource;
return this;
}
public String id() {
return mappedStatement.id;
}
public Builder parameterMap(ParameterMap parameterMap) {
mappedStatement.parameterMap = parameterMap;
return this;
}
public Builder resultMaps(List resultMaps) {
mappedStatement.resultMaps = resultMaps;
for (ResultMap resultMap : resultMaps) {
mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
}
return this;
}
public Builder fetchSize(Integer fetchSize) {
mappedStatement.fetchSize = fetchSize;
return this;
}
public Builder timeout(Integer timeout) {
mappedStatement.timeout = timeout;
return this;
}
public Builder statementType(StatementType statementType) {
mappedStatement.statementType = statementType;
return this;
}
public Builder resultSetType(ResultSetType resultSetType) {
mappedStatement.resultSetType = resultSetType == null ? ResultSetType.DEFAULT : resultSetType;
return this;
}
public Builder cache(Cache cache) {
mappedStatement.cache = cache;
return this;
}
public Builder flushCacheRequired(boolean flushCacheRequired) {
mappedStatement.flushCacheRequired = flushCacheRequired;
return this;
}
public Builder useCache(boolean useCache) {
mappedStatement.useCache = useCache;
return this;
}
public Builder resultOrdered(boolean resultOrdered) {
mappedStatement.resultOrdered = resultOrdered;
return this;
}
public Builder keyGenerator(KeyGenerator keyGenerator) {
mappedStatement.keyGenerator = keyGenerator;
return this;
}
public Builder keyProperty(String keyProperty) {
mappedStatement.keyProperties = delimitedStringToArray(keyProperty);
return this;
}
public Builder keyColumn(String keyColumn) {
mappedStatement.keyColumns = delimitedStringToArray(keyColumn);
return this;
}
public Builder databaseId(String databaseId) {
mappedStatement.databaseId = databaseId;
return this;
}
public Builder lang(LanguageDriver driver) {
mappedStatement.lang = driver;
return this;
}
public Builder resultSets(String resultSet) {
mappedStatement.resultSets = delimitedStringToArray(resultSet);
return this;
}
/**
* @deprecated Use {@link #resultSets}
*/
@Deprecated
public Builder resulSets(String resultSet) {
mappedStatement.resultSets = delimitedStringToArray(resultSet);
return this;
}
public MappedStatement build() {
assert mappedStatement.configuration != null;
assert mappedStatement.id != null;
assert mappedStatement.sqlSource != null;
assert mappedStatement.lang != null;
mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
return mappedStatement;
}
}
public KeyGenerator getKeyGenerator() {
return keyGenerator;
}
public SqlCommandType getSqlCommandType() {
return sqlCommandType;
}
public String getResource() {
return resource;
}
public Configuration getConfiguration() {
return configuration;
}
public String getId() {
return id;
}
public boolean hasNestedResultMaps() {
return hasNestedResultMaps;
}
public Integer getFetchSize() {
return fetchSize;
}
public Integer getTimeout() {
return timeout;
}
public StatementType getStatementType() {
return statementType;
}
public ResultSetType getResultSetType() {
return resultSetType;
}
public SqlSource getSqlSource() {
return sqlSource;
}
public ParameterMap getParameterMap() {
return parameterMap;
}
public List getResultMaps() {
return resultMaps;
}
public Cache getCache() {
return cache;
}
public boolean isFlushCacheRequired() {
return flushCacheRequired;
}
public boolean isUseCache() {
return useCache;
}
public boolean isResultOrdered() {
return resultOrdered;
}
public String getDatabaseId() {
return databaseId;
}
public String[] getKeyProperties() {
return keyProperties;
}
public String[] getKeyColumns() {
return keyColumns;
}
public Log getStatementLog() {
return statementLog;
}
public LanguageDriver getLang() {
return lang;
}
public String[] getResultSets() {
return resultSets;
}
/**
* @deprecated Use {@link #getResultSets()}
*/
@Deprecated
public String[] getResulSets() {
return resultSets;
}
/**
* 构建BoundSql对象
* @param parameterObject 参数对象
* @return BoundSql对象
*/
public BoundSql getBoundSql(Object parameterObject) {
//获取可执行的SQL
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List parameterMappings = boundSql.getParameterMappings();
//将参数映射加入到BoundSql,重新构建BoundSql对象
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
//检查是否有嵌套的resultMap
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
private static String[] delimitedStringToArray(String in) {
if (in == null || in.trim().length() == 0) {
return null;
} else {
return in.split(",");
}
}
}
ResultMapping
/**
* 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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* resultMap 子元素映射关系
* @author Clinton Begin
*/
public class ResultMapping {
/**
* mybtais全局配置
*/
private Configuration configuration;
/**
* 属性名
*/
private String property;
/**
* 列名
*/
private String column;
/**
* 属性的java类型
*/
private Class> javaType;
/**
* 属性的jdbc类型
*/
private JdbcType jdbcType;
/**
* 类型转换器
*/
private TypeHandler> typeHandler;
/**
* 嵌套的resultMapId,【命名空间+resultMapId】
*/
private String nestedResultMapId;
/**
* 嵌套的selectId,【命名空间+selectId】
*/
private String nestedQueryId;
/**
* 不能为空的列名
*/
private Set notNullColumns;
/**
* SQL的列名前缀
*/
private String columnPrefix;
/**
* 属性标记
*/
private List flags;
/**
* 组合ResultMaping列表
*/
private List composites;
/**
* 指定用于加载复杂类型的结果集名字。
*/
private String resultSet;
/**
* 指定外键对应的列名
*/
private String foreignColumn;
/**
* 是否懒加载
*/
private boolean lazy;
ResultMapping() {
}
public static class Builder {
private ResultMapping resultMapping = new ResultMapping();
/**
*
* @param configuration mybatis全局配置信息
* @param property 属性名
* @param column 表列名
* @param typeHandler 类型处理器
*/
public Builder(Configuration configuration, String property, String column, TypeHandler> typeHandler) {
this(configuration, property);
resultMapping.column = column;
resultMapping.typeHandler = typeHandler;
}
/**
*
* @param configuration mybatis全局配置信息
* @param property 属性名
* @param column 表列名
* @param javaType 属性的java类型
*/
public Builder(Configuration configuration, String property, String column, Class> javaType) {
this(configuration, property);
resultMapping.column = column;
resultMapping.javaType = javaType;
}
/**
*
* - {@link #resultMapping#flags}初始化空的{@link ArrayList}
* - {@link #resultMapping#composites}初始化空的{@link ArrayList}
* - @{@link #resultMapping#lazy}初始为mybatis全局配置文件的 {@link Configuration#lazyLoadingEnabled} 属性设置
*
* @param configuration mybatis全局配置信息
* @param property 属性名
*/
public Builder(Configuration configuration, String property) {
resultMapping.configuration = configuration;
resultMapping.property = property;
resultMapping.flags = new ArrayList<>();
resultMapping.composites = new ArrayList<>();
resultMapping.lazy = configuration.isLazyLoadingEnabled();
}
/**
* 属性的java类型
*/
public Builder javaType(Class> javaType) {
resultMapping.javaType = javaType;
return this;
}
/**
* 属性的jdbc类型
*/
public Builder jdbcType(JdbcType jdbcType) {
resultMapping.jdbcType = jdbcType;
return this;
}
/**
* 嵌套的resultMapId
*/
public Builder nestedResultMapId(String nestedResultMapId) {
resultMapping.nestedResultMapId = nestedResultMapId;
return this;
}
/**
* 嵌套的selectId
*/
public Builder nestedQueryId(String nestedQueryId) {
resultMapping.nestedQueryId = nestedQueryId;
return this;
}
/**
* 集合的多结果集
*/
public Builder resultSet(String resultSet) {
resultMapping.resultSet = resultSet;
return this;
}
/**
* 指定外键对应的列名
*/
public Builder foreignColumn(String foreignColumn) {
resultMapping.foreignColumn = foreignColumn;
return this;
}
/**
* 获取指定的不为空才创建实例的列
*/
public Builder notNullColumns(Set notNullColumns) {
resultMapping.notNullColumns = notNullColumns;
return this;
}
/**
* 列前缀
*/
public Builder columnPrefix(String columnPrefix) {
resultMapping.columnPrefix = columnPrefix;
return this;
}
/**
* 属性标记
*/
public Builder flags(List flags) {
resultMapping.flags = flags;
return this;
}
/**
* 类型处理器
*/
public Builder typeHandler(TypeHandler> typeHandler) {
resultMapping.typeHandler = typeHandler;
return this;
}
/**
* 组合RequestMapping列表
*/
public Builder composites(List composites) {
resultMapping.composites = composites;
return this;
}
/**
* 懒加载
*/
public Builder lazy(boolean lazy) {
resultMapping.lazy = lazy;
return this;
}
/**
* 构建出ResuatMapping实例。
*
* resultMapping在Builder初始化时已经创建了,这个方法用于返回resultMapping实例。
* 而且该方法会对flags,composites进行加锁,以防止在其他调用者修改。在typeHandler
* 没有设置的情况下,也会在全局typeHandler注册器中找到对应javaType的typeHandlder实例。
* 也验证了一下属性设置的合法性,将不合法的属性设置抛出异常。
*
* @return
*/
public ResultMapping build() {
// lock down collections 锁住集合,让flags,composites集合不能再发生任何更改。
resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
//当typeHandler为null,会根据javaType来获取一个typeHandler实例赋值到typeHandler
resolveTypeHandler();
//验证,不符合条件的,就会抛出异常中断执行。
validate();
return resultMapping;
}
/**
* 验证,不符合下面的某一条件都会抛出 {@link IllegalStateException} 异常。
*
* - 不能同时定义 嵌套的selectId 和 嵌套的resultMapId
* - 在没有设置嵌套的selectId有没有设置嵌套的resultMapId的情况下,typeHandler不应该没有设置或者获取不到。
* - 列名是可选的在嵌套的resultMap组,但是不能在没有设置嵌套的resultMapId又没有设置组合的resultMap列表的情况下。
* - 有结果集结合的情况下,通过','隔开的列名与外键列名数保持一致。
*
*/
private void validate() {
// Issue #697: cannot define both nestedQueryId and nestedResultMapId
//不能同时定义 嵌套的selectId 和 嵌套的resultMapId
if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
throw new IllegalStateException("Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
}
// Issue #5: there should be no mappings without typehandler
//在没有设置嵌套的selectId有没有设置嵌套的resultMapId的情况下,typeHandler不应该没有设置或者获取不到。
if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null && resultMapping.typeHandler == null) {
throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
}
// Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest
//列名是可选的在嵌套的resultMap组,但是不能在没有设置嵌套的resultMapId又没有设置组合的resultMap列表的情况下。
if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) {
throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
}
//有结果集结合的情况下,通过','隔开的列名与外键列名数保持一致。
if (resultMapping.getResultSet() != null) {
int numColumns = 0;
if (resultMapping.column != null) {
numColumns = resultMapping.column.split(",").length;
}
int numForeignColumns = 0;
if (resultMapping.foreignColumn != null) {
numForeignColumns = resultMapping.foreignColumn.split(",").length;
}
if (numColumns != numForeignColumns) {
throw new IllegalStateException("There should be the same number of columns and foreignColumns in property " + resultMapping.property);
}
}
}
/**
* 解析TypeHadnler实例
*
* 当 {@link #typeHandler} 的为null时,该方法就会根据 {@link #javaType}去
* {@link #configuration}的TypeHandlerResgistry对应的typeHandler实例并赋值到{@link #typeHandler}。
*
*/
private void resolveTypeHandler() {
if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
Configuration configuration = resultMapping.configuration;
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
}
}
public Builder column(String column) {
resultMapping.column = column;
return this;
}
}
public String getProperty() {
return property;
}
public String getColumn() {
return column;
}
public Class> getJavaType() {
return javaType;
}
public JdbcType getJdbcType() {
return jdbcType;
}
public TypeHandler> getTypeHandler() {
return typeHandler;
}
public String getNestedResultMapId() {
return nestedResultMapId;
}
public String getNestedQueryId() {
return nestedQueryId;
}
public Set getNotNullColumns() {
return notNullColumns;
}
public String getColumnPrefix() {
return columnPrefix;
}
public List getFlags() {
return flags;
}
public List getComposites() {
return composites;
}
/**
* 是否存在组合ResultMaping列表
* @return
*/
public boolean isCompositeResult() {
return this.composites != null && !this.composites.isEmpty();
}
public String getResultSet() {
return this.resultSet;
}
public String getForeignColumn() {
return foreignColumn;
}
public void setForeignColumn(String foreignColumn) {
this.foreignColumn = foreignColumn;
}
public boolean isLazy() {
return lazy;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
/**
* 判断属性名{@link #property}
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ResultMapping that = (ResultMapping) o;
if (property == null || !property.equals(that.property)) {
return false;
}
return true;
}
/**
* 判断属性名{@link #property}
*/
@Override
public int hashCode() {
if (property != null) {
return property.hashCode();
} else if (column != null) {
return column.hashCode();
} else {
return 0;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ResultMapping{");
//sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
sb.append("property='").append(property).append('\'');
sb.append(", column='").append(column).append('\'');
sb.append(", javaType=").append(javaType);
sb.append(", jdbcType=").append(jdbcType);
//sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\'');
sb.append(", nestedQueryId='").append(nestedQueryId).append('\'');
sb.append(", notNullColumns=").append(notNullColumns);
sb.append(", columnPrefix='").append(columnPrefix).append('\'');
sb.append(", flags=").append(flags);
sb.append(", composites=").append(composites);
sb.append(", resultSet='").append(resultSet).append('\'');
sb.append(", foreignColumn='").append(foreignColumn).append('\'');
sb.append(", lazy=").append(lazy);
sb.append('}');
return sb.toString();
}
}