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

mybatis / mybatis-3 / 2673

25 Jan 2025 11:34PM UTC coverage: 87.155% (-0.1%) from 87.27%
2673

Pull #3388

github

web-flow
Merge 0e0798685 into 1b2346c8b
Pull Request #3388: Support nested cursor

3622 of 4421 branches covered (81.93%)

11 of 31 new or added lines in 2 files covered. (35.48%)

2 existing lines in 1 file now uncovered.

9560 of 10969 relevant lines covered (87.15%)

0.87 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-2025 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
import org.apache.ibatis.type.JdbcType;
34

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

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

55
  private ResultMap() {
56
  }
57

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

274
}
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

© 2026 Coveralls, Inc