• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / mybatis-3 / 2604

03 Jan 2025 10:00AM UTC coverage: 87.524% (+0.3%) from 87.177%
2604

Pull #3146

github

web-flow
Merge 60c1f5fea into 8ac3920af
Pull Request #3146: Shared ambiguity instance

3633 of 4401 branches covered (82.55%)

4 of 4 new or added lines in 1 file covered. (100.0%)

254 existing lines in 22 files now uncovered.

9569 of 10933 relevant lines covered (87.52%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.96
/src/main/java/org/apache/ibatis/mapping/ResultMap.java
1
/*
2
 *    Copyright 2009-2024 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
15
 */
16
package org.apache.ibatis.mapping;
17

18
import java.lang.annotation.Annotation;
19
import java.lang.reflect.Constructor;
20
import java.util.ArrayList;
21
import java.util.Collections;
22
import java.util.HashSet;
23
import java.util.List;
24
import java.util.Locale;
25
import java.util.Set;
26

27
import org.apache.ibatis.annotations.Param;
28
import org.apache.ibatis.builder.BuilderException;
29
import org.apache.ibatis.logging.Log;
30
import org.apache.ibatis.logging.LogFactory;
31
import org.apache.ibatis.reflection.ParamNameUtil;
32
import org.apache.ibatis.session.Configuration;
33

34
/**
35
 * @author Clinton Begin
36
 */
37
public class ResultMap {
38
  private Configuration configuration;
39

40
  private String id;
41
  private Class<?> type;
42
  private List<ResultMapping> resultMappings;
43
  private List<ResultMapping> idResultMappings;
44
  private List<ResultMapping> constructorResultMappings;
45
  private List<ResultMapping> propertyResultMappings;
46
  private Set<String> mappedColumns;
47
  private Set<String> mappedProperties;
48
  private Discriminator discriminator;
49
  private boolean hasResultMapsUsingConstructorCollection;
50
  private boolean hasNestedResultMaps;
51
  private boolean hasNestedQueries;
52
  private Boolean autoMapping;
53

54
  private ResultMap() {
55
  }
56

57
  public static class Builder {
58
    private static final Log log = LogFactory.getLog(Builder.class);
1✔
59

60
    private final ResultMap resultMap = new ResultMap();
1✔
61

62
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
63
      this(configuration, id, type, resultMappings, null);
1✔
64
    }
1✔
65

66
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings,
67
        Boolean autoMapping) {
1✔
68
      resultMap.configuration = configuration;
1✔
69
      resultMap.id = id;
1✔
70
      resultMap.type = type;
1✔
71
      resultMap.resultMappings = resultMappings;
1✔
72
      resultMap.autoMapping = autoMapping;
1✔
73
    }
1✔
74

75
    public Builder discriminator(Discriminator discriminator) {
76
      resultMap.discriminator = discriminator;
1✔
77
      return this;
1✔
78
    }
79

80
    public Class<?> type() {
UNCOV
81
      return resultMap.type;
×
82
    }
83

84
    public ResultMap build() {
85
      if (resultMap.id == null) {
1!
UNCOV
86
        throw new IllegalArgumentException("ResultMaps must have an id");
×
87
      }
88
      resultMap.mappedColumns = new HashSet<>();
1✔
89
      resultMap.mappedProperties = new HashSet<>();
1✔
90
      resultMap.idResultMappings = new ArrayList<>();
1✔
91
      resultMap.constructorResultMappings = new ArrayList<>();
1✔
92
      resultMap.propertyResultMappings = new ArrayList<>();
1✔
93
      final List<String> constructorArgNames = new ArrayList<>();
1✔
94
      for (ResultMapping resultMapping : resultMap.resultMappings) {
1✔
95
        resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
1✔
96
        resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps
1✔
97
            || resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null;
1✔
98
        final String column = resultMapping.getColumn();
1✔
99
        if (column != null) {
1✔
100
          resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
1✔
101
        } else if (resultMapping.isCompositeResult()) {
1✔
102
          for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
1✔
103
            final String compositeColumn = compositeResultMapping.getColumn();
1✔
104
            if (compositeColumn != null) {
1!
105
              resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
1✔
106
            }
107
          }
1✔
108
        }
109
        final String property = resultMapping.getProperty();
1✔
110
        if (property != null) {
1✔
111
          resultMap.mappedProperties.add(property);
1✔
112
        }
113
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
1✔
114
          resultMap.constructorResultMappings.add(resultMapping);
1✔
115

116
          // #101
117
          Class<?> javaType = resultMapping.getJavaType();
1✔
118
          resultMap.hasResultMapsUsingConstructorCollection = resultMap.hasResultMapsUsingConstructorCollection
1✔
119
              || (resultMapping.getNestedQueryId() == null && javaType != null
1!
120
                  && resultMap.configuration.getObjectFactory().isCollection(javaType));
1✔
121

122
          if (resultMapping.getProperty() != null) {
1✔
123
            constructorArgNames.add(resultMapping.getProperty());
1✔
124
          }
125
        } else {
1✔
126
          resultMap.propertyResultMappings.add(resultMapping);
1✔
127
        }
128
        if (resultMapping.getFlags().contains(ResultFlag.ID)) {
1✔
129
          resultMap.idResultMappings.add(resultMapping);
1✔
130
        }
131
      }
1✔
132
      if (resultMap.idResultMappings.isEmpty()) {
1✔
133
        resultMap.idResultMappings.addAll(resultMap.resultMappings);
1✔
134
      }
135
      if (!constructorArgNames.isEmpty()) {
1✔
136
        final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
1✔
137
        if (actualArgNames == null) {
1✔
138
          throw new BuilderException("Error in result map '" + resultMap.id + "'. Failed to find a constructor in '"
1✔
139
              + resultMap.getType().getName() + "' with arg names " + constructorArgNames
1✔
140
              + ". 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.");
141
        }
142
        resultMap.constructorResultMappings.sort((o1, o2) -> {
1✔
143
          int paramIdx1 = actualArgNames.indexOf(o1.getProperty());
1✔
144
          int paramIdx2 = actualArgNames.indexOf(o2.getProperty());
1✔
145
          return paramIdx1 - paramIdx2;
1✔
146
        });
147
      }
148
      // lock down collections
149
      resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
1✔
150
      resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
1✔
151
      resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
1✔
152
      resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
1✔
153
      resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
1✔
154
      return resultMap;
1✔
155
    }
156

157
    private List<String> argNamesOfMatchingConstructor(List<String> constructorArgNames) {
158
      Constructor<?>[] constructors = resultMap.type.getDeclaredConstructors();
1✔
159
      for (Constructor<?> constructor : constructors) {
1✔
160
        Class<?>[] paramTypes = constructor.getParameterTypes();
1✔
161
        if (constructorArgNames.size() == paramTypes.length) {
1✔
162
          List<String> paramNames = getArgNames(constructor);
1✔
163
          if (constructorArgNames.containsAll(paramNames)
1✔
164
              && argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
1✔
165
            return paramNames;
1✔
166
          }
167
        }
168
      }
169
      return null;
1✔
170
    }
171

172
    private boolean argTypesMatch(final List<String> constructorArgNames, Class<?>[] paramTypes,
173
        List<String> paramNames) {
174
      for (int i = 0; i < constructorArgNames.size(); i++) {
1✔
175
        Class<?> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
1✔
176
        Class<?> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
1✔
177
        if (!actualType.equals(specifiedType)) {
1✔
178
          if (log.isDebugEnabled()) {
1!
UNCOV
179
            log.debug("While building result map '" + resultMap.id + "', found a constructor with arg names "
×
UNCOV
180
                + constructorArgNames + ", but the type of '" + constructorArgNames.get(i)
×
UNCOV
181
                + "' did not match. Specified: [" + specifiedType.getName() + "] Declared: [" + actualType.getName()
×
182
                + "]");
183
          }
184
          return false;
1✔
185
        }
186
      }
187
      return true;
1✔
188
    }
189

190
    private List<String> getArgNames(Constructor<?> constructor) {
191
      List<String> paramNames = new ArrayList<>();
1✔
192
      List<String> actualParamNames = null;
1✔
193
      final Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
1✔
194
      int paramCount = paramAnnotations.length;
1✔
195
      for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
1✔
196
        String name = null;
1✔
197
        for (Annotation annotation : paramAnnotations[paramIndex]) {
1✔
198
          if (annotation instanceof Param) {
1!
199
            name = ((Param) annotation).value();
1✔
200
            break;
1✔
201
          }
202
        }
203
        if (name == null && resultMap.configuration.isUseActualParamName()) {
1!
204
          if (actualParamNames == null) {
1✔
205
            actualParamNames = ParamNameUtil.getParamNames(constructor);
1✔
206
          }
207
          if (actualParamNames.size() > paramIndex) {
1!
208
            name = actualParamNames.get(paramIndex);
1✔
209
          }
210
        }
211
        paramNames.add(name != null ? name : "arg" + paramIndex);
1!
212
      }
213
      return paramNames;
1✔
214
    }
215
  }
216

217
  public String getId() {
218
    return id;
1✔
219
  }
220

221
  public boolean hasResultMapsUsingConstructorCollection() {
222
    return hasResultMapsUsingConstructorCollection;
1✔
223
  }
224

225
  public boolean hasNestedResultMaps() {
226
    return hasNestedResultMaps;
1✔
227
  }
228

229
  public boolean hasNestedQueries() {
UNCOV
230
    return hasNestedQueries;
×
231
  }
232

233
  public Class<?> getType() {
234
    return type;
1✔
235
  }
236

237
  public List<ResultMapping> getResultMappings() {
238
    return resultMappings;
1✔
239
  }
240

241
  public List<ResultMapping> getConstructorResultMappings() {
242
    return constructorResultMappings;
1✔
243
  }
244

245
  public List<ResultMapping> getPropertyResultMappings() {
246
    return propertyResultMappings;
1✔
247
  }
248

249
  public List<ResultMapping> getIdResultMappings() {
250
    return idResultMappings;
1✔
251
  }
252

253
  public Set<String> getMappedColumns() {
254
    return mappedColumns;
1✔
255
  }
256

257
  public Set<String> getMappedProperties() {
258
    return mappedProperties;
1✔
259
  }
260

261
  public Discriminator getDiscriminator() {
262
    return discriminator;
1✔
263
  }
264

265
  public void forceNestedResultMaps() {
266
    hasNestedResultMaps = true;
1✔
267
  }
1✔
268

269
  public Boolean getAutoMapping() {
270
    return autoMapping;
1✔
271
  }
272

273
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc