Mybatis3.5.1源碼分析
- Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源碼解析
- Mybatis-Configuration源碼解析
- Mybatis-事務(wù)對象源碼解析
- Mybatis-數(shù)據(jù)源源碼解析
- Mybatis緩存策略源碼解析
- Mybatis-DatabaseIdProvider源碼解析
- Mybatis-TypeHandler源碼解析
- Mybatis-Reflector源碼解析
- Mybatis-ObjectFactory,ObjectWrapperFactory源碼分析
- Mybatis-Mapper各類標簽封裝類源碼解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
- Mybatis-MapperAnnotationBuilder源碼分析
- [Mybatis-MetaObject,MetaClass源碼解析]http://www.itdecent.cn/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;
/**
* 參數(shù)映射信息封裝類,Mapper.xml的parameterMap標簽
* @author Clinton Begin
*/
public class ParameterMap {
/**
* ParameterMap標簽的ID屬性
*/
private String id;
/**
* ParamterMap標簽的type屬性,即參數(shù)類型
*/
private Class<?> type;
/**
* ParamterMap標簽的所有paramter標簽,即參數(shù)的所有屬性列表
*/
private List<ParameterMapping> parameterMappings;
private ParameterMap() {
}
/**
* ParameterMap的構(gòu)造器
*/
public static class Builder {
private ParameterMap parameterMap = new ParameterMap();
/**
*
* @param configuration 這個參數(shù)已經(jīng)不用了,很有可能后面的版本去掉
* @param id ParameterMap標簽的ID屬性
* @param type ParamterMap標簽的type屬性,即參數(shù)類型
* @param parameterMappings ParamterMap標簽的所有paramter標簽,即參數(shù)的所有屬性列表
*/
public Builder(Configuration configuration, String id, Class<?> type, List<ParameterMapping> parameterMappings) {
parameterMap.id = id;
parameterMap.type = type;
parameterMap.parameterMappings = parameterMappings;
}
/**
* 返回參數(shù)類型
*/
public Class<?> type() {
return parameterMap.type;
}
public ParameterMap build() {
//lock down collections 鎖定集合
/**
* Collections.unmodifiableList方法會將傳進來的集合變成只讀的集合,
* 只要改變該集合都會拋出UnsupportedOperationException。
* 總之,可以保證對象的list內(nèi)容不被意料之外地修改,保證對象的封裝性
*/
parameterMap.parameterMappings = Collections.unmodifiableList(parameterMap.parameterMappings);
return parameterMap;
}
}
public String getId() {
return id;
}
public Class<?> getType() {
return type;
}
public List<ParameterMapping> 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;
/**
* 參數(shù)映射
* <P>
* 對應(yīng)于Mapper.xml的:
* <parameter property="" resultMap="" javaType="" typeHandler="" jdbcType="" mode="" scale=""></parameter>
* </P>
* <p>
* 參考博客:<a >https://blog.csdn.net/L_Sail/article/details/73824674</a>
* </p>
* @author Clinton Begin
*/
public class ParameterMapping {
private Configuration configuration;
/**
* 屬性名
*/
private String property;
/**
* mode 屬性允許你指定 IN,OUT 或 INOUT 參數(shù)。如果參數(shù)為 OUT 或 INOUT,
* 參數(shù)對象屬性的真實值將會被改變,就像你在獲取輸出參數(shù)時所期望的那樣。
* 如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),
* 必須指定一個 resultMap 來映射結(jié)果集到參數(shù)類型
*/
private ParameterMode mode;
/**
* java類型
*/
private Class<?> javaType = Object.class;
/**
* jdbc類型
*/
private JdbcType jdbcType;
/**
* 小數(shù)點后保留的位數(shù)
*/
private Integer numericScale;
/**
* 類型處理器
*/
private TypeHandler<?> typeHandler;
/**
* 結(jié)果映射ID
*/
private String resultMapId;
/**
* jdbc類型名, SQL 結(jié)構(gòu)類型的完全限定名稱
* <p>
* 作用于CallableStatement.registerOutParameter (int parameterIndex, int sqlType, String typeName)方法的
* typeName參數(shù):<br/>
* 在執(zhí)行存儲過程調(diào)用之前,必須顯式調(diào)用 registerOutParameter 為每個 OUT 參數(shù)注冊來自 java.sql.Types 的類型。
* 對于用戶定義的參數(shù),還應(yīng)該提供該參數(shù)的完全限定 SQL 類型名稱,而 REF 參數(shù)則要求提供所引用類型的完全限定類型名稱。
* 不需要類型代碼和類型名稱信息的 JDBC 驅(qū)動程序可以忽略它。不過,為了便于移植,應(yīng)用程序應(yīng)該為用戶定義的參數(shù)和 REF 參數(shù)提供這些值。
* </p>
*/
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 參數(shù)。如果參數(shù)為 OUT 或 INOUT,參數(shù)對象屬性的真實值將會被改變,就像你在獲取輸出參數(shù)時所期望的那樣。
* 如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結(jié)果集到參數(shù)類型
*/
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;
}
/**
* 參數(shù)的屬性的小數(shù)點保留的位數(shù)
*/
public Builder numericScale(Integer numericScale) {
parameterMapping.numericScale = numericScale;
return this;
}
/**
* 參數(shù)的屬性對應(yīng)的結(jié)果映射ID
*/
public Builder resultMapId(String resultMapId) {
parameterMapping.resultMapId = resultMapId;
return this;
}
/**
* 參數(shù)的屬性對應(yīng)的TypeHandler
*/
public Builder typeHandler(TypeHandler<?> typeHandler) {
parameterMapping.typeHandler = typeHandler;
return this;
}
/**
* 參數(shù)的屬性對應(yīng)的jdbcType名
*/
public Builder jdbcTypeName(String jdbcTypeName) {
parameterMapping.jdbcTypeName = jdbcTypeName;
return this;
}
/**
* 參數(shù)的屬性正則表達式
*/
public Builder expression(String expression) {
parameterMapping.expression = expression;
return this;
}
/**
* 構(gòu)建ParameterMapping
* <p>
* 在調(diào)用其ParameterMapping.Builder的構(gòu)造方法時,就已經(jīng)構(gòu)建了parameterMapping。
* 而方法時就是返回這個paramterMapping,在返回之前,會檢查有沒有設(shè)置TypeHandler,沒有的
* 話,會嘗試根據(jù)javaType從TypeHandler注冊器中獲取對應(yīng)javaType的TypeHandler實例.
* 然后 檢查一下業(yè)務(wù)需求所需要的屬性是否缺漏,缺漏的拋出異常。如
* </p>
* <ol style='margin-top:0px'>
* <li>如果是javaType為ResultSet的時候,需要設(shè)置resultMapId屬性,因為需要根據(jù)resultMap來映射獲取數(shù)據(jù)。
* 否則拋出異常</li>
* <li> 如果是javaType為其他類型是,必須要有對應(yīng)其類型的typeHandler的實例。否則拋出異常</li>
* </ol>
*/
public ParameterMapping build() {
//解析獲取TypeHandler實例
resolveTypeHandler();
// 檢查一下業(yè)務(wù)需求所需要的屬性是否缺漏,缺漏的拋出異常
validate();
return parameterMapping;
}
/**
* 檢查一下業(yè)務(wù)需求所需要的屬性是否缺漏
* <p>
* 如果是javaType為ResultSet的時候,需要設(shè)置resultMapId屬性,因為需要根據(jù)resultMap來映射獲取數(shù)據(jù)。
* 否則拋出異常
* </p>
* <p>
* 如果是javaType為其他類型是,必須要有對應(yīng)其類型的typeHandler的實例。否則拋出異常
* </p>
*/
private void validate() {
//當jdbcType為JdbcType.CURSOR是,javaType就會是ResultSet
if (ResultSet.class.equals(parameterMapping.javaType)) {
//如果是ResultSet就必須要有resultMapId,因為需要根據(jù)resultMap來映射獲取數(shù)據(jù)。
if (parameterMapping.resultMapId == null) {
throw new IllegalStateException("Missing resultmap in property '"
+ parameterMapping.property + "'. "
+ "Parameters of type java.sql.ResultSet require a resultmap.");
}
} else {
//javaType為其他類型時,必須要有對應(yīng)其類型的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實例
* <p>
* 在typeHandler為null且有javaType的情況下才會嘗試從TypeHandler注冊器獲取對應(yīng)的TypeHandler實例
* </p>
*/
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;
/**
* 結(jié)果映射信息封裝類,Mapper.xml的resultMap標簽信息封裝類對象
* @author Clinton Begin
*/
public class ResultMap {
private Configuration configuration;
/**
* resultMap標簽的Id屬性
*/
private String id;
/**
* resultMap標簽的type屬性,resultMap的java類型
*/
private Class<?> type;
/**
* 結(jié)果映射
*/
private List<ResultMapping> resultMappings;
/**
* id結(jié)果映射
*/
private List<ResultMapping> idResultMappings;
/**
* 構(gòu)造函數(shù)結(jié)果映射,
* <p>
* 存放著帶有構(gòu)造函數(shù)標記ResultFlag.CONSTRUCTOR的resultMapping
* </p>
*/
private List<ResultMapping> constructorResultMappings;
/**
* 屬性結(jié)果映射。
* <p>將沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping添加到propertyResultMapping,
* 沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping其實可以認為就是resultMap標簽
* 下的result標簽和id標簽</p>
*/
private List<ResultMapping> propertyResultMappings;
/**
* 映射列
* <p>
* {@link #resultMappings}的所有column屬性集合,包括嵌套的reusltMapping的column,
* 所保存的列是以大寫形式保存
* </p>
*/
private Set<String> mappedColumns;
/**
* 映射屬性,存放著所有resultMapping的所有propery
*/
private Set<String> mappedProperties;
/**
* 鑒別器
*/
private Discriminator discriminator;
/**
* 有嵌套映射結(jié)果,判斷reusltMapping中有沒有設(shè)置嵌套的 resultMap id
*/
private boolean hasNestedResultMaps;
/**
* 有嵌套查詢,判斷reusltMapping中有沒有設(shè)置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 結(jié)果元素映射集合
*/
public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
this(configuration, id, type, resultMappings, null);
}
/**
*
* @param configuration mybatis全局配置
* @param id resultMap標簽的ID屬性
* @param type resultMap標簽的type屬性,resultMap的java類型
* @param resultMappings 結(jié)果元素映射集合
* @param autoMapping 自動映射
*/
public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> 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;
}
/**
* 構(gòu)建resultMap
*
*/
public ResultMap build() {
//驗證Id,Id是找到這個reusltMap的關(guān)鍵,不可以沒有。
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<>();
//存放著帶有構(gòu)造函數(shù)標記的ResultMapping的property
final List<String> 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);
}
//將帶有構(gòu)造函數(shù)標記ResultFlag.CONSTRUCTOR的resultMapping添加到constructorResultMapping
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
resultMap.constructorResultMappings.add(resultMapping);
//添加帶有構(gòu)造函數(shù)標記ResultFlag.CONSTRUCTOR的resultMapping的property
if (resultMapping.getProperty() != null) {
constructorArgNames.add(resultMapping.getProperty());
}
} else {
/**
*將沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping添加到propertyResultMapping,
* 沒有構(gòu)造函數(shù)標記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()) {
/**
* 獲取對應(yīng)construtorArgNames的構(gòu)造函數(shù)的參數(shù)名列表,這里忽略constructorArgNames的元素順序是否和
* 對應(yīng)的構(gòu)造函數(shù)的參數(shù)順序?qū)?yīng),只對應(yīng)他們的類型和參數(shù)名
*/
final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
/**
* 這個異常觸發(fā)條件我覺得會很容易觸發(fā)的。
* 1.在如果在mybatis-config.xml文件中設(shè)置了useActualParamName為false的時候,resultMap對應(yīng)的
* java類型的構(gòu)造函數(shù)的參數(shù)又沒有加上@param注解時,就會導(dǎo)致和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根據(jù)構(gòu)造函數(shù)的參數(shù)名列表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;
}
/**
* 匹配構(gòu)造函數(shù)的參數(shù)名稱
* @param constructorArgNames 帶有構(gòu)造函數(shù)標記的ResultMapping的property集合
* @return 返回已經(jīng)匹配驗證成功的構(gòu)造函數(shù)的參數(shù)名稱,在匹配驗證不成功的時候會返回null
*/
private List<String> argNamesOfMatchingConstructor(List<String> constructorArgNames) {
Constructor<?>[] constructors = resultMap.type.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
Class<?>[] paramTypes = constructor.getParameterTypes();
if (constructorArgNames.size() == paramTypes.length) {
List<String> paramNames = getArgNames(constructor);
/**
* paramNames有可能返回args0,args1...,這個時候containsAll返回false,使得整個方法返回null。
* 這里argTypesMatch方法并不會接收到paramNames為args0,args1...的情況,因為這里這里使用'&&',
* 在containAll返回false的時候,是不會執(zhí)行argTypesMatch方法。
*/
if (constructorArgNames.containsAll(paramNames)
&& argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
return paramNames;
}
}
}
return null;
}
/**
* 匹配構(gòu)造函數(shù)的參數(shù)類型
* @param constructorArgNames 帶有構(gòu)造函數(shù)標記的ResultMapping的property集合
* @param paramTypes 構(gòu)造函數(shù)的所有參數(shù)的類型
* @param paramNames 構(gòu)造函數(shù)的所有參數(shù)的參數(shù)名
* @return 在 {@link #resultMap#constructorResultMappings} 的所有javaType與
* paramTypes取得的類型在類型上都相等的情況下就會返回true
*/
private boolean argTypesMatch(final List<String> constructorArgNames,
Class<?>[] paramTypes, List<String> paramNames) {
for (int i = 0; i < constructorArgNames.size(); i++) {
/**
* 從constructorArgNames獲取i位置的property,再從paramNames獲取property對應(yīng)的位置,
* 再從paramTypes取得該位置對應(yīng)的類型
*/
Class<?> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
//從構(gòu)造函數(shù)ResultMapping中獲取i位置的ResultMapping的javaType
Class<?> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
/**
* 這里判斷不對應(yīng)的情況下是不會拋出異常,只會打印log,并返回false,之所以沒有拋出異常時是因為
* 統(tǒng)一交給了build方法拋出異常,這里只是簡單的調(diào)用Class#equal方法,Class并沒有對
* equal方法進行重寫,所以equal方法相當于'=='的判斷結(jié)果,也就是說這里沒有考慮到父子類的情況。
*
* 這里不存在因為順序不一致而導(dǎo)致不相等的情況,因為actualType的通過constructorArgNames的位置獲取的,
* 而constructorArgNames和resultMap.constructorResultMappings同一個循環(huán)遍歷下填充元素的,所以
* 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;
}
/**
* 獲取構(gòu)造函數(shù)的參數(shù)名
* @param constructor {@link #type} 的構(gòu)造函數(shù)
* @return
* <ol style='margin-top:0px'>
* <li>嘗試獲取 {@code constructor} 的參數(shù)的 {@link Param} 作為參數(shù)名。</li>
* <li>在開啟了 {@link #configuration#useActualParamName} 情況下,嘗試獲取 {@code constructor} 的參數(shù)名作為參數(shù)名</li>
* <li>最后方案:用"arg" + 參數(shù)索引作為參數(shù)名</li>
* </ol>
*/
private List<String> getArgNames(Constructor<?> constructor) {
List<String> paramNames = new ArrayList<>();
List<String> actualParamNames = null;
//獲取constructor每個參數(shù)的注解
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情況下,會使用在構(gòu)造函數(shù)的參數(shù)名賦值給name
if (name == null && resultMap.configuration.isUseActualParamName()) {
if (actualParamNames == null) {
actualParamNames = ParamNameUtil.getParamNames(constructor);
}
//要保證構(gòu)造函數(shù)的參數(shù)名數(shù)量是大于參數(shù)索引paramIndex,否則在獲取參數(shù)名時會拋出異常
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<ResultMapping> getResultMappings() {
return resultMappings;
}
public List<ResultMapping> getConstructorResultMappings() {
return constructorResultMappings;
}
public List<ResultMapping> getPropertyResultMappings() {
return propertyResultMappings;
}
public List<ResultMapping> getIdResultMappings() {
return idResultMappings;
}
public Set<String> getMappedColumns() {
return mappedColumns;
}
public Set<String> 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;
/**
* 鑒別器
* 用于對一個查詢可能出現(xiàn)的不同結(jié)果集做鑒別,根據(jù)具體的條件為 resultMap 動態(tài)選擇返回值類型。
* <p>
* eg:<br/>
* <discriminator javaType="int" column="type"><br/>
* <case value="1" resultType="Apple"/><br/>
* <case value="2" resultType="Banana"/><br/>
* <discriminator/>
* </p>
*
* <p>
* 參考博客:<a >https://blog.csdn.net/weixin_36210698/article/details/83508113</a>
* </p>
* @author Clinton Begin
*/
public class Discriminator {
/**
* ResutMap標簽的屬性標簽封裝
*/
private ResultMapping resultMapping;
/**
* case標簽信息封裝,映射
* <p>
* key=case標簽的value屬性,value=命名空間+case標簽的resultMap屬性(reusltMapId),
* </p>
*/
private Map<String, String> discriminatorMap;
Discriminator() {
}
public static class Builder {
private Discriminator discriminator = new Discriminator();
public Builder(Configuration configuration, ResultMapping resultMapping, Map<String, String> 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<String, String> 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;
/**
* <p>
* Mapper.xml文件的select,delete,update,insert這些DML標簽的封裝類
* </p>
* <p>
* MappedStatement類在Mybatis框架中用于表示XML文件中一個sql語句節(jié)點,
* 即一個、或者標簽。Mybatis框架在初始化階段會對XML配置文件進行讀取
* ,將其中的sql語句節(jié)點對象化為一個個MappedStatement對象。
* </p>
* @author Clinton Begin
*/
public final class MappedStatement {
/**
* 資源路徑,當前項目,有可能是java文件也有可能是.xml文件
*/
private String resource;
/**
* mybatis全局配置信息
*/
private Configuration configuration;
/**
* 節(jié)點中的id屬性加要命名空間
*/
private String id;
/**
* <p>select標簽的fetchSize屬性</p>
* <br/>
* <p>
* 這是嘗試影響驅(qū)動程序每次批量返回的結(jié)果行數(shù)和這個設(shè)置值相等。默認值為 unset(依賴驅(qū)動)。
* </p>
* <br/>
* <p>
* 假設(shè)fetchSize為5,查詢總記錄數(shù)為100,每批5次返回給你,最后結(jié)果還是總記錄數(shù),只是提高點查詢的速度而已
* </p>
* <br/>
* <p>
* MySQL不支持fetchSize,默認為一次性取出所有數(shù)據(jù)。所以容易導(dǎo)致OOM,
* 如果是Oracle的話就是默認取出fetchSize條數(shù)據(jù)。
* 裸露JDBC防止OOM可以調(diào)用statement的enableStreamingResults方法,
* MyBatis應(yīng)該在<select fetchSize="-2147483648">。
* </p>
*/
private Integer fetchSize;
/**
* 這個設(shè)置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)。
* 默認值為 unset(依賴驅(qū)動)。
*/
private Integer timeout;
/**
* <ol>
* <li>STATEMENT:對應(yīng)于Statement對象,有SQL注入的風(fēng)險</li>
* <li>PREPARED:PreparedStatement,預(yù)編譯處理</li>
* <li>CALLABLE:CallableStatement一般調(diào)用存儲過程的時候使用</li>
* </ol>
* 默認是PREPARED
*/
private StatementType statementType;
/**
* 其實的就是{@link java.sql.ResultSet}的常量,只不過Mybatis對其常量進行封裝成枚舉。
* <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>
*/
private ResultSetType resultSetType;
/**
* 構(gòu)建動態(tài)SQL
*/
private SqlSource sqlSource;
/**
* 每條語句都對就一個緩存,如果有的話
*/
private Cache cache;
/**
* 參數(shù)映射信息封裝類,Mapper.xml的parameterMap標簽
*/
private ParameterMap parameterMap;
/**
* 結(jié)果映射信息封裝類,Mapper.xml的resultMap標簽 集合
*/
private List<ResultMap> resultMaps;
/**
* 需要刷新緩存標記
* <p>
* 如果配置了flushCacheRequired為true,則會在執(zhí)行器執(zhí)行之前就清空本地一級緩存,換句話說就是關(guān)閉一級緩存功能
* </p>
*/
private boolean flushCacheRequired;
/**
* 啟用緩存標記
*/
private boolean useCache;
/**
* select標簽
* <p>
* 如果為 true,就是假設(shè)包含了嵌套結(jié)果集或是分組了,這樣的話當返回一個主結(jié)果行的時候,
* 就不會發(fā)生有對前面結(jié)果集的引用的情況。這就使得在獲取嵌套的結(jié)果集的時候不至于導(dǎo)致內(nèi)存不夠用。
* 默認值:false。
* </p>
* <p>
* 參考博客:<a >https://blog.csdn.net/isea533/article/details/51533296?utm_source=blogxgwz9</a>
* </p>
*/
private boolean resultOrdered;
/**
* SQL指令類型
* <ol>
* <li>UNKNOWN:未知</li>
* <li>INSERT:插入</li>
* <li>UPDATE:修改</li>
* <li>DELETE:刪除</li>
* <li>SELECT:查詢</li>
* <li>FLUSH:刷新</li>
* </ol>
*/
private SqlCommandType sqlCommandType;
/**
* Key生成器
*/
private KeyGenerator keyGenerator;
/**
* (僅對 insert 和 update 有用)唯一標記一個屬性,
* MyBatis 會通過 getGeneratedKeys 的返回值或者通過 insert 語句的 selectKey 子元素設(shè)置它的鍵值,
* 默認:unset。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
*/
private String[] keyProperties;
/**
* (僅對 insert 和 update 有用)通過生成的鍵值設(shè)置表中的列名,
* 這個設(shè)置僅在某些數(shù)據(jù)庫(像 PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設(shè)置。
* 如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表
*/
private String[] keyColumns;
/**
* 有嵌套映射結(jié)果標記,這個標記除了ParamterMap里面的resultMap外還保護ParamterMap里面的resultMap的resultMap
*/
private boolean hasNestedResultMaps;
/**
* DML標簽中配置的databaseId屬性
*/
private String databaseId;
/**
* 日志對象
*/
private Log statementLog;
/**
* 語言驅(qū)動,默認是 {@link org.apache.ibatis.scripting.xmltags.XMLLanguageDriver}
*/
private LanguageDriver lang;
/**
* 結(jié)果集
*/
private String[] resultSets;
MappedStatement() {
// constructor disabled
}
public static class Builder {
/**
* 默認對象
*/
private MappedStatement mappedStatement = new MappedStatement();
/**
*
* @param configuration mybatis全局配置信息
* @param id 節(jié)點中的id屬性加要命名空間
* @param sqlSource 構(gòu)建動態(tài)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<ResultMap> 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<ResultMap> 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;
}
/**
* 構(gòu)建BoundSql對象
* @param parameterObject 參數(shù)對象
* @return BoundSql對象
*/
public BoundSql getBoundSql(Object parameterObject) {
//獲取可執(zhí)行的SQL
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//將參數(shù)映射加入到BoundSql,重新構(gòu)建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 子元素映射關(guān)系
* @author Clinton Begin
*/
public class ResultMapping {
/**
* mybtais全局配置
*/
private Configuration configuration;
/**
* 屬性名
*/
private String property;
/**
* 列名
*/
private String column;
/**
* 屬性的java類型
*/
private Class<?> javaType;
/**
* 屬性的jdbc類型
*/
private JdbcType jdbcType;
/**
* 類型轉(zhuǎn)換器
*/
private TypeHandler<?> typeHandler;
/**
* 嵌套的resultMapId,【命名空間+resultMapId】
*/
private String nestedResultMapId;
/**
* 嵌套的selectId,【命名空間+selectId】
*/
private String nestedQueryId;
/**
* 不能為空的列名
*/
private Set<String> notNullColumns;
/**
* SQL的列名前綴
*/
private String columnPrefix;
/**
* 屬性標記
*/
private List<ResultFlag> flags;
/**
* 組合ResultMaping列表
*/
private List<ResultMapping> composites;
/**
* 指定用于加載復(fù)雜類型的結(jié)果集名字。
*/
private String resultSet;
/**
* 指定外鍵對應(yīng)的列名
*/
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;
}
/**
* <ol>
* <li>{@link #resultMapping#flags}初始化空的{@link ArrayList}</li>
* <li>{@link #resultMapping#composites}初始化空的{@link ArrayList}</li>
* <li>@{@link #resultMapping#lazy}初始為mybatis全局配置文件的 {@link Configuration#lazyLoadingEnabled} 屬性設(shè)置</li>
* </ol>
* @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;
}
/**
* 集合的多結(jié)果集
*/
public Builder resultSet(String resultSet) {
resultMapping.resultSet = resultSet;
return this;
}
/**
* 指定外鍵對應(yīng)的列名
*/
public Builder foreignColumn(String foreignColumn) {
resultMapping.foreignColumn = foreignColumn;
return this;
}
/**
* 獲取指定的不為空才創(chuàng)建實例的列
*/
public Builder notNullColumns(Set<String> notNullColumns) {
resultMapping.notNullColumns = notNullColumns;
return this;
}
/**
* 列前綴
*/
public Builder columnPrefix(String columnPrefix) {
resultMapping.columnPrefix = columnPrefix;
return this;
}
/**
* 屬性標記
*/
public Builder flags(List<ResultFlag> flags) {
resultMapping.flags = flags;
return this;
}
/**
* 類型處理器
*/
public Builder typeHandler(TypeHandler<?> typeHandler) {
resultMapping.typeHandler = typeHandler;
return this;
}
/**
* 組合RequestMapping列表
*/
public Builder composites(List<ResultMapping> composites) {
resultMapping.composites = composites;
return this;
}
/**
* 懶加載
*/
public Builder lazy(boolean lazy) {
resultMapping.lazy = lazy;
return this;
}
/**
* 構(gòu)建出ResuatMapping實例。
* <p>
* resultMapping在Builder初始化時已經(jīng)創(chuàng)建了,這個方法用于返回resultMapping實例。
* 而且該方法會對flags,composites進行加鎖,以防止在其他調(diào)用者修改。在typeHandler
* 沒有設(shè)置的情況下,也會在全局typeHandler注冊器中找到對應(yīng)javaType的typeHandlder實例。
* 也驗證了一下屬性設(shè)置的合法性,將不合法的屬性設(shè)置拋出異常。
* </p>
* @return
*/
public ResultMapping build() {
// lock down collections 鎖住集合,讓flags,composites集合不能再發(fā)生任何更改。
resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
//當typeHandler為null,會根據(jù)javaType來獲取一個typeHandler實例賦值到typeHandler
resolveTypeHandler();
//驗證,不符合條件的,就會拋出異常中斷執(zhí)行。
validate();
return resultMapping;
}
/**
* 驗證,不符合下面的某一條件都會拋出 {@link IllegalStateException} 異常。
* <ol style='margin-top:0px'>
* <li>不能同時定義 嵌套的selectId 和 嵌套的resultMapId</li>
* <li>在沒有設(shè)置嵌套的selectId有沒有設(shè)置嵌套的resultMapId的情況下,typeHandler不應(yīng)該沒有設(shè)置或者獲取不到。</li>
* <li>列名是可選的在嵌套的resultMap組,但是不能在沒有設(shè)置嵌套的resultMapId又沒有設(shè)置組合的resultMap列表的情況下。</li>
* <li>有結(jié)果集結(jié)合的情況下,通過','隔開的列名與外鍵列名數(shù)保持一致。</li>
* </ol>
*/
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
//在沒有設(shè)置嵌套的selectId有沒有設(shè)置嵌套的resultMapId的情況下,typeHandler不應(yīng)該沒有設(shè)置或者獲取不到。
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組,但是不能在沒有設(shè)置嵌套的resultMapId又沒有設(shè)置組合的resultMap列表的情況下。
if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) {
throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
}
//有結(jié)果集結(jié)合的情況下,通過','隔開的列名與外鍵列名數(shù)保持一致。
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實例
* <p>
* 當 {@link #typeHandler} 的為null時,該方法就會根據(jù) {@link #javaType}去
* {@link #configuration}的TypeHandlerResgistry對應(yīng)的typeHandler實例并賦值到{@link #typeHandler}。
* </p>
*/
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<String> getNotNullColumns() {
return notNullColumns;
}
public String getColumnPrefix() {
return columnPrefix;
}
public List<ResultFlag> getFlags() {
return flags;
}
public List<ResultMapping> 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();
}
}