ResultMap.java
- /*
- * Copyright 2009-2024 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
- *
- * https://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;
- /**
- * @author Clinton Begin
- */
- public class ResultMap {
- private Configuration configuration;
- private String id;
- private Class<?> type;
- private List<ResultMapping> resultMappings;
- private List<ResultMapping> idResultMappings;
- private List<ResultMapping> constructorResultMappings;
- private List<ResultMapping> propertyResultMappings;
- private Set<String> mappedColumns;
- private Set<String> mappedProperties;
- private Discriminator discriminator;
- private boolean hasNestedResultMaps;
- private boolean hasNestedQueries;
- private Boolean autoMapping;
- private ResultMap() {
- }
- public static class Builder {
- private static final Log log = LogFactory.getLog(Builder.class);
- private final ResultMap resultMap = new ResultMap();
- public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
- this(configuration, id, type, resultMappings, null);
- }
- 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;
- }
- public Builder discriminator(Discriminator discriminator) {
- resultMap.discriminator = discriminator;
- return this;
- }
- public Class<?> type() {
- return resultMap.type;
- }
- public ResultMap build() {
- 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<>();
- final List<String> constructorArgNames = new ArrayList<>();
- for (ResultMapping resultMapping : resultMap.resultMappings) {
- resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
- resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps
- || resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null;
- 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));
- }
- }
- }
- final String property = resultMapping.getProperty();
- if (property != null) {
- resultMap.mappedProperties.add(property);
- }
- if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
- resultMap.constructorResultMappings.add(resultMapping);
- if (resultMapping.getProperty() != null) {
- constructorArgNames.add(resultMapping.getProperty());
- }
- } else {
- resultMap.propertyResultMappings.add(resultMapping);
- }
- if (resultMapping.getFlags().contains(ResultFlag.ID)) {
- resultMap.idResultMappings.add(resultMapping);
- }
- }
- if (resultMap.idResultMappings.isEmpty()) {
- resultMap.idResultMappings.addAll(resultMap.resultMappings);
- }
- if (!constructorArgNames.isEmpty()) {
- final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
- if (actualArgNames == null) {
- throw new BuilderException("Error in result map '" + resultMap.id + "'. Failed to find a constructor in '"
- + resultMap.getType().getName() + "' with arg names " + constructorArgNames
- + ". Note that 'javaType' is required when there is no writable property with the same name ('name' is optional, BTW). There might be more info in debug log.");
- }
- 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;
- }
- 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);
- if (constructorArgNames.containsAll(paramNames)
- && argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
- return paramNames;
- }
- }
- }
- return null;
- }
- private boolean argTypesMatch(final List<String> constructorArgNames, Class<?>[] paramTypes,
- List<String> paramNames) {
- for (int i = 0; i < constructorArgNames.size(); i++) {
- Class<?> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
- Class<?> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
- 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;
- }
- private List<String> getArgNames(Constructor<?> constructor) {
- List<String> paramNames = new ArrayList<>();
- List<String> actualParamNames = null;
- final Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
- int paramCount = paramAnnotations.length;
- for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
- String name = null;
- for (Annotation annotation : paramAnnotations[paramIndex]) {
- if (annotation instanceof Param) {
- name = ((Param) annotation).value();
- break;
- }
- }
- if (name == null && resultMap.configuration.isUseActualParamName()) {
- if (actualParamNames == null) {
- actualParamNames = ParamNameUtil.getParamNames(constructor);
- }
- if (actualParamNames.size() > paramIndex) {
- name = actualParamNames.get(paramIndex);
- }
- }
- 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;
- }
- }