1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.executor.resultset;
17
18 import java.lang.reflect.Constructor;
19 import java.sql.CallableStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.sql.Statement;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.ibatis.annotations.AutomapConstructor;
32 import org.apache.ibatis.binding.MapperMethod.ParamMap;
33 import org.apache.ibatis.cache.CacheKey;
34 import org.apache.ibatis.cursor.Cursor;
35 import org.apache.ibatis.cursor.defaults.DefaultCursor;
36 import org.apache.ibatis.executor.ErrorContext;
37 import org.apache.ibatis.executor.Executor;
38 import org.apache.ibatis.executor.ExecutorException;
39 import org.apache.ibatis.executor.loader.ResultLoader;
40 import org.apache.ibatis.executor.loader.ResultLoaderMap;
41 import org.apache.ibatis.executor.parameter.ParameterHandler;
42 import org.apache.ibatis.executor.result.DefaultResultContext;
43 import org.apache.ibatis.executor.result.DefaultResultHandler;
44 import org.apache.ibatis.executor.result.ResultMapException;
45 import org.apache.ibatis.mapping.BoundSql;
46 import org.apache.ibatis.mapping.Discriminator;
47 import org.apache.ibatis.mapping.MappedStatement;
48 import org.apache.ibatis.mapping.ParameterMapping;
49 import org.apache.ibatis.mapping.ParameterMode;
50 import org.apache.ibatis.mapping.ResultMap;
51 import org.apache.ibatis.mapping.ResultMapping;
52 import org.apache.ibatis.reflection.MetaClass;
53 import org.apache.ibatis.reflection.MetaObject;
54 import org.apache.ibatis.reflection.ReflectorFactory;
55 import org.apache.ibatis.reflection.factory.ObjectFactory;
56 import org.apache.ibatis.session.AutoMappingBehavior;
57 import org.apache.ibatis.session.Configuration;
58 import org.apache.ibatis.session.ResultContext;
59 import org.apache.ibatis.session.ResultHandler;
60 import org.apache.ibatis.session.RowBounds;
61 import org.apache.ibatis.type.JdbcType;
62 import org.apache.ibatis.type.TypeHandler;
63 import org.apache.ibatis.type.TypeHandlerRegistry;
64
65
66
67
68
69
70
71 public class DefaultResultSetHandler implements ResultSetHandler {
72
73 private static final Object DEFERRED = new Object();
74
75 private final Executor executor;
76 private final Configuration configuration;
77 private final MappedStatement mappedStatement;
78 private final RowBounds rowBounds;
79 private final ParameterHandler parameterHandler;
80 private final ResultHandler<?> resultHandler;
81 private final BoundSql boundSql;
82 private final TypeHandlerRegistry typeHandlerRegistry;
83 private final ObjectFactory objectFactory;
84 private final ReflectorFactory reflectorFactory;
85
86
87 private final Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
88 private final Map<String, Object> ancestorObjects = new HashMap<>();
89 private Object previousRowValue;
90
91
92 private final Map<String, ResultMapping> nextResultMaps = new HashMap<>();
93 private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();
94
95
96 private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();
97
98
99 private boolean useConstructorMappings;
100
101 private static class PendingRelation {
102 public MetaObject metaObject;
103 public ResultMapping propertyMapping;
104 }
105
106 private static class UnMappedColumnAutoMapping {
107 private final String column;
108 private final String property;
109 private final TypeHandler<?> typeHandler;
110 private final boolean primitive;
111
112 public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
113 this.column = column;
114 this.property = property;
115 this.typeHandler = typeHandler;
116 this.primitive = primitive;
117 }
118 }
119
120 public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql,
121 RowBounds rowBounds) {
122 this.executor = executor;
123 this.configuration = mappedStatement.getConfiguration();
124 this.mappedStatement = mappedStatement;
125 this.rowBounds = rowBounds;
126 this.parameterHandler = parameterHandler;
127 this.boundSql = boundSql;
128 this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
129 this.objectFactory = configuration.getObjectFactory();
130 this.reflectorFactory = configuration.getReflectorFactory();
131 this.resultHandler = resultHandler;
132 }
133
134
135
136
137
138 @Override
139 public void handleOutputParameters(CallableStatement cs) throws SQLException {
140 final Object parameterObject = parameterHandler.getParameterObject();
141 final MetaObject metaParam = configuration.newMetaObject(parameterObject);
142 final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
143 for (int i = 0; i < parameterMappings.size(); i++) {
144 final ParameterMapping parameterMapping = parameterMappings.get(i);
145 if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
146 if (ResultSet.class.equals(parameterMapping.getJavaType())) {
147 handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
148 } else {
149 final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
150 metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
151 }
152 }
153 }
154 }
155
156 private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException {
157 if (rs == null) {
158 return;
159 }
160 try {
161 final String resultMapId = parameterMapping.getResultMapId();
162 final ResultMap resultMap = configuration.getResultMap(resultMapId);
163 final ResultSetWrappertset/ResultSetWrapper.html#ResultSetWrapper">ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);
164 if (this.resultHandler == null) {
165 final DefaultResultHandlertHandler.html#DefaultResultHandler">DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory);
166 handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
167 metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
168 } else {
169 handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
170 }
171 } finally {
172
173 closeResultSet(rs);
174 }
175 }
176
177
178
179
180 @Override
181 public List<Object> handleResultSets(Statement stmt) throws SQLException {
182 ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
183
184 final List<Object> multipleResults = new ArrayList<>();
185
186 int resultSetCount = 0;
187 ResultSetWrapper rsw = getFirstResultSet(stmt);
188
189 List<ResultMap> resultMaps = mappedStatement.getResultMaps();
190 int resultMapCount = resultMaps.size();
191 validateResultMapsCount(rsw, resultMapCount);
192 while (rsw != null && resultMapCount > resultSetCount) {
193 ResultMap resultMap = resultMaps.get(resultSetCount);
194 handleResultSet(rsw, resultMap, multipleResults, null);
195 rsw = getNextResultSet(stmt);
196 cleanUpAfterHandlingResultSet();
197 resultSetCount++;
198 }
199
200 String[] resultSets = mappedStatement.getResultSets();
201 if (resultSets != null) {
202 while (rsw != null && resultSetCount < resultSets.length) {
203 ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
204 if (parentMapping != null) {
205 String nestedResultMapId = parentMapping.getNestedResultMapId();
206 ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
207 handleResultSet(rsw, resultMap, null, parentMapping);
208 }
209 rsw = getNextResultSet(stmt);
210 cleanUpAfterHandlingResultSet();
211 resultSetCount++;
212 }
213 }
214
215 return collapseSingleResultList(multipleResults);
216 }
217
218 @Override
219 public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
220 ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());
221
222 ResultSetWrapper rsw = getFirstResultSet(stmt);
223
224 List<ResultMap> resultMaps = mappedStatement.getResultMaps();
225
226 int resultMapCount = resultMaps.size();
227 validateResultMapsCount(rsw, resultMapCount);
228 if (resultMapCount != 1) {
229 throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
230 }
231
232 ResultMap resultMap = resultMaps.get(0);
233 return new DefaultCursor<>(this, resultMap, rsw, rowBounds);
234 }
235
236 private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
237 ResultSet rs = stmt.getResultSet();
238 while (rs == null) {
239
240
241 if (stmt.getMoreResults()) {
242 rs = stmt.getResultSet();
243 } else {
244 if (stmt.getUpdateCount() == -1) {
245
246 break;
247 }
248 }
249 }
250 return rs != null ? new ResultSetWrapper(rs, configuration) : null;
251 }
252
253 private ResultSetWrapper getNextResultSet(Statement stmt) {
254
255 try {
256 if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
257
258 if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
259 ResultSet rs = stmt.getResultSet();
260 if (rs == null) {
261 return getNextResultSet(stmt);
262 } else {
263 return new ResultSetWrapper(rs, configuration);
264 }
265 }
266 }
267 } catch (Exception e) {
268
269 }
270 return null;
271 }
272
273 private void closeResultSet(ResultSet rs) {
274 try {
275 if (rs != null) {
276 rs.close();
277 }
278 } catch (SQLException e) {
279
280 }
281 }
282
283 private void cleanUpAfterHandlingResultSet() {
284 nestedResultObjects.clear();
285 }
286
287 private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
288 if (rsw != null && resultMapCount < 1) {
289 throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
290 + "'. It's likely that neither a Result Type nor a Result Map was specified.");
291 }
292 }
293
294 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
295 try {
296 if (parentMapping != null) {
297 handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
298 } else {
299 if (resultHandler == null) {
300 DefaultResultHandlerr.html#DefaultResultHandler">DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
301 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
302 multipleResults.add(defaultResultHandler.getResultList());
303 } else {
304 handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
305 }
306 }
307 } finally {
308
309 closeResultSet(rsw.getResultSet());
310 }
311 }
312
313 @SuppressWarnings("unchecked")
314 private List<Object> collapseSingleResultList(List<Object> multipleResults) {
315 return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
316 }
317
318
319
320
321
322 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
323 if (resultMap.hasNestedResultMaps()) {
324 ensureNoRowBounds();
325 checkResultHandler();
326 handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
327 } else {
328 handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
329 }
330 }
331
332 private void ensureNoRowBounds() {
333 if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
334 throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. "
335 + "Use safeRowBoundsEnabled=false setting to bypass this check.");
336 }
337 }
338
339 protected void checkResultHandler() {
340 if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered()) {
341 throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
342 + "Use safeResultHandlerEnabled=false setting to bypass this check "
343 + "or ensure your statement returns ordered data and set resultOrdered=true on it.");
344 }
345 }
346
347 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
348 throws SQLException {
349 DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
350 ResultSet resultSet = rsw.getResultSet();
351 skipRows(resultSet, rowBounds);
352 while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
353 ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
354 Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
355 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
356 }
357 }
358
359 private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
360 if (parentMapping != null) {
361 linkToParents(rs, parentMapping, rowValue);
362 } else {
363 callResultHandler(resultHandler, resultContext, rowValue);
364 }
365 }
366
367 @SuppressWarnings("unchecked" )
368 private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
369 resultContext.nextResultObject(rowValue);
370 ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
371 }
372
373 private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
374 return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
375 }
376
377 private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
378 if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
379 if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
380 rs.absolute(rowBounds.getOffset());
381 }
382 } else {
383 for (int i = 0; i < rowBounds.getOffset(); i++) {
384 if (!rs.next()) {
385 break;
386 }
387 }
388 }
389 }
390
391
392
393
394
395 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
396 final ResultLoaderMapltLoaderMap.html#ResultLoaderMap">ResultLoaderMap lazyLoader = new ResultLoaderMap();
397 Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
398 if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
399 final MetaObject metaObject = configuration.newMetaObject(rowValue);
400 boolean foundValues = this.useConstructorMappings;
401 if (shouldApplyAutomaticMappings(resultMap, false)) {
402 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
403 }
404 foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
405 foundValues = lazyLoader.size() > 0 || foundValues;
406 rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
407 }
408 return rowValue;
409 }
410
411 private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
412 if (resultMap.getAutoMapping() != null) {
413 return resultMap.getAutoMapping();
414 } else {
415 if (isNested) {
416 return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
417 } else {
418 return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
419 }
420 }
421 }
422
423
424
425
426
427 private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
428 throws SQLException {
429 final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
430 boolean foundValues = false;
431 final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
432 for (ResultMapping propertyMapping : propertyMappings) {
433 String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
434 if (propertyMapping.getNestedResultMapId() != null) {
435
436 column = null;
437 }
438 if (propertyMapping.isCompositeResult()
439 || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
440 || propertyMapping.getResultSet() != null) {
441 Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
442
443 final String property = propertyMapping.getProperty();
444 if (property == null) {
445 continue;
446 } else if (value == DEFERRED) {
447 foundValues = true;
448 continue;
449 }
450 if (value != null) {
451 foundValues = true;
452 }
453 if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
454
455 metaObject.setValue(property, value);
456 }
457 }
458 }
459 return foundValues;
460 }
461
462 private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
463 throws SQLException {
464 if (propertyMapping.getNestedQueryId() != null) {
465 return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
466 } else if (propertyMapping.getResultSet() != null) {
467 addPendingChildRelation(rs, metaResultObject, propertyMapping);
468 return DEFERRED;
469 } else {
470 final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
471 final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
472 return typeHandler.getResult(rs, column);
473 }
474 }
475
476 private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
477 final String mapKey = resultMap.getId() + ":" + columnPrefix;
478 List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
479 if (autoMapping == null) {
480 autoMapping = new ArrayList<>();
481 final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
482 for (String columnName : unmappedColumnNames) {
483 String propertyName = columnName;
484 if (columnPrefix != null && !columnPrefix.isEmpty()) {
485
486
487 if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
488 propertyName = columnName.substring(columnPrefix.length());
489 } else {
490 continue;
491 }
492 }
493 final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
494 if (property != null && metaObject.hasSetter(property)) {
495 if (resultMap.getMappedProperties().contains(property)) {
496 continue;
497 }
498 final Class<?> propertyType = metaObject.getSetterType(property);
499 if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
500 final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
501 autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
502 } else {
503 configuration.getAutoMappingUnknownColumnBehavior()
504 .doAction(mappedStatement, columnName, property, propertyType);
505 }
506 } else {
507 configuration.getAutoMappingUnknownColumnBehavior()
508 .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
509 }
510 }
511 autoMappingsCache.put(mapKey, autoMapping);
512 }
513 return autoMapping;
514 }
515
516 private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
517 List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
518 boolean foundValues = false;
519 if (!autoMapping.isEmpty()) {
520 for (UnMappedColumnAutoMapping mapping : autoMapping) {
521 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
522 if (value != null) {
523 foundValues = true;
524 }
525 if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
526
527 metaObject.setValue(mapping.property, value);
528 }
529 }
530 }
531 return foundValues;
532 }
533
534
535
536 private void linkToParents(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException {
537 CacheKey parentKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getForeignColumn());
538 List<PendingRelation> parents = pendingRelations.get(parentKey);
539 if (parents != null) {
540 for (PendingRelation parent : parents) {
541 if (parent != null && rowValue != null) {
542 linkObjects(parent.metaObject, parent.propertyMapping, rowValue);
543 }
544 }
545 }
546 }
547
548 private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException {
549 CacheKey cacheKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());
550 PendingRelation deferLoad = new PendingRelation();
551 deferLoad.metaObject = metaResultObject;
552 deferLoad.propertyMapping = parentMapping;
553 List<PendingRelation> relations = pendingRelations.computeIfAbsent(cacheKey, k -> new ArrayList<>());
554
555 relations.add(deferLoad);
556 ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
557 if (previous == null) {
558 nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
559 } else {
560 if (!previous.equals(parentMapping)) {
561 throw new ExecutorException("Two different properties are mapped to the same resultSet");
562 }
563 }
564 }
565
566 private CacheKey createKeyForMultipleResults(ResultSet rs, ResultMapping resultMapping, String names, String columns) throws SQLException {
567 CacheKeyKey.html#CacheKey">CacheKey cacheKey = new CacheKey();
568 cacheKey.update(resultMapping);
569 if (columns != null && names != null) {
570 String[] columnsArray = columns.split(",");
571 String[] namesArray = names.split(",");
572 for (int i = 0; i < columnsArray.length; i++) {
573 Object value = rs.getString(columnsArray[i]);
574 if (value != null) {
575 cacheKey.update(namesArray[i]);
576 cacheKey.update(value);
577 }
578 }
579 }
580 return cacheKey;
581 }
582
583
584
585
586
587 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
588 this.useConstructorMappings = false;
589 final List<Class<?>> constructorArgTypes = new ArrayList<>();
590 final List<Object> constructorArgs = new ArrayList<>();
591 Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
592 if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
593 final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
594 for (ResultMapping propertyMapping : propertyMappings) {
595
596 if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
597 resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
598 break;
599 }
600 }
601 }
602 this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
603 return resultObject;
604 }
605
606 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
607 throws SQLException {
608 final Class<?> resultType = resultMap.getType();
609 final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
610 final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
611 if (hasTypeHandlerForResultObject(rsw, resultType)) {
612 return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
613 } else if (!constructorMappings.isEmpty()) {
614 return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
615 } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
616 return objectFactory.create(resultType);
617 } else if (shouldApplyAutomaticMappings(resultMap, false)) {
618 return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
619 }
620 throw new ExecutorException("Do not know how to create an instance of " + resultType);
621 }
622
623 Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
624 List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
625 boolean foundValues = false;
626 for (ResultMapping constructorMapping : constructorMappings) {
627 final Class<?> parameterType = constructorMapping.getJavaType();
628 final String column = constructorMapping.getColumn();
629 final Object value;
630 try {
631 if (constructorMapping.getNestedQueryId() != null) {
632 value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
633 } else if (constructorMapping.getNestedResultMapId() != null) {
634 final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
635 value = getRowValue(rsw, resultMap, getColumnPrefix(columnPrefix, constructorMapping));
636 } else {
637 final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
638 value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
639 }
640 } catch (ResultMapException | SQLException e) {
641 throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
642 }
643 constructorArgTypes.add(parameterType);
644 constructorArgs.add(value);
645 foundValues = value != null || foundValues;
646 }
647 return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
648 }
649
650 private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
651 final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
652 final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
653 if (defaultConstructor != null) {
654 return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
655 } else {
656 for (Constructor<?> constructor : constructors) {
657 if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
658 return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
659 }
660 }
661 }
662 throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
663 }
664
665 private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor) throws SQLException {
666 boolean foundValues = false;
667 for (int i = 0; i < constructor.getParameterTypes().length; i++) {
668 Class<?> parameterType = constructor.getParameterTypes()[i];
669 String columnName = rsw.getColumnNames().get(i);
670 TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
671 Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
672 constructorArgTypes.add(parameterType);
673 constructorArgs.add(value);
674 foundValues = value != null || foundValues;
675 }
676 return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
677 }
678
679 private Constructor<?> findDefaultConstructor(final Constructor<?>[] constructors) {
680 if (constructors.length == 1) {
681 return constructors[0];
682 }
683
684 for (final Constructor<?> constructor : constructors) {
685 if (constructor.isAnnotationPresent(AutomapConstructor.class)) {
686 return constructor;
687 }
688 }
689 return null;
690 }
691
692 private boolean allowedConstructorUsingTypeHandlers(final Constructor<?> constructor, final List<JdbcType> jdbcTypes) {
693 final Class<?>[] parameterTypes = constructor.getParameterTypes();
694 if (parameterTypes.length != jdbcTypes.size()) {
695 return false;
696 }
697 for (int i = 0; i < parameterTypes.length; i++) {
698 if (!typeHandlerRegistry.hasTypeHandler(parameterTypes[i], jdbcTypes.get(i))) {
699 return false;
700 }
701 }
702 return true;
703 }
704
705 private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
706 final Class<?> resultType = resultMap.getType();
707 final String columnName;
708 if (!resultMap.getResultMappings().isEmpty()) {
709 final List<ResultMapping> resultMappingList = resultMap.getResultMappings();
710 final ResultMapping mapping = resultMappingList.get(0);
711 columnName = prependPrefix(mapping.getColumn(), columnPrefix);
712 } else {
713 columnName = rsw.getColumnNames().get(0);
714 }
715 final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
716 return typeHandler.getResult(rsw.getResultSet(), columnName);
717 }
718
719
720
721
722
723 private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
724 final String nestedQueryId = constructorMapping.getNestedQueryId();
725 final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
726 final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
727 final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
728 Object value = null;
729 if (nestedQueryParameterObject != null) {
730 final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
731 final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
732 final Class<?> targetType = constructorMapping.getJavaType();
733 final ResultLoaderultLoader.html#ResultLoader">ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
734 value = resultLoader.loadResult();
735 }
736 return value;
737 }
738
739 private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
740 throws SQLException {
741 final String nestedQueryId = propertyMapping.getNestedQueryId();
742 final String property = propertyMapping.getProperty();
743 final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
744 final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
745 final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
746 Object value = null;
747 if (nestedQueryParameterObject != null) {
748 final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
749 final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
750 final Class<?> targetType = propertyMapping.getJavaType();
751 if (executor.isCached(nestedQuery, key)) {
752 executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
753 value = DEFERRED;
754 } else {
755 final ResultLoaderultLoader.html#ResultLoader">ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
756 if (propertyMapping.isLazy()) {
757 lazyLoader.addLoader(property, metaResultObject, resultLoader);
758 value = DEFERRED;
759 } else {
760 value = resultLoader.loadResult();
761 }
762 }
763 }
764 return value;
765 }
766
767 private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
768 if (resultMapping.isCompositeResult()) {
769 return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);
770 } else {
771 return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
772 }
773 }
774
775 private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
776 final TypeHandler<?> typeHandler;
777 if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
778 typeHandler = typeHandlerRegistry.getTypeHandler(parameterType);
779 } else {
780 typeHandler = typeHandlerRegistry.getUnknownTypeHandler();
781 }
782 return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
783 }
784
785 private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
786 final Object parameterObject = instantiateParameterObject(parameterType);
787 final MetaObject metaObject = configuration.newMetaObject(parameterObject);
788 boolean foundValues = false;
789 for (ResultMapping innerResultMapping : resultMapping.getComposites()) {
790 final Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
791 final TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propType);
792 final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
793
794 if (propValue != null) {
795 metaObject.setValue(innerResultMapping.getProperty(), propValue);
796 foundValues = true;
797 }
798 }
799 return foundValues ? parameterObject : null;
800 }
801
802 private Object instantiateParameterObject(Class<?> parameterType) {
803 if (parameterType == null) {
804 return new HashMap<>();
805 } else if (ParamMap.class.equals(parameterType)) {
806 return new HashMap<>();
807 } else {
808 return objectFactory.create(parameterType);
809 }
810 }
811
812
813
814
815
816 public ResultMaping/ResultMap.html#ResultMap">ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
817 Set<String> pastDiscriminators = new HashSet<>();
818 Discriminator discriminator = resultMap.getDiscriminator();
819 while (discriminator != null) {
820 final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
821 final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
822 if (configuration.hasResultMap(discriminatedMapId)) {
823 resultMap = configuration.getResultMap(discriminatedMapId);
824 Discriminator lastDiscriminator = discriminator;
825 discriminator = resultMap.getDiscriminator();
826 if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
827 break;
828 }
829 } else {
830 break;
831 }
832 }
833 return resultMap;
834 }
835
836 private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {
837 final ResultMapping resultMapping = discriminator.getResultMapping();
838 final TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
839 return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
840 }
841
842 private String prependPrefix(String columnName, String prefix) {
843 if (columnName == null || columnName.length() == 0 || prefix == null || prefix.length() == 0) {
844 return columnName;
845 }
846 return prefix + columnName;
847 }
848
849
850
851
852
853 private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
854 final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
855 ResultSet resultSet = rsw.getResultSet();
856 skipRows(resultSet, rowBounds);
857 Object rowValue = previousRowValue;
858 while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
859 final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
860 final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
861 Object partialObject = nestedResultObjects.get(rowKey);
862
863 if (mappedStatement.isResultOrdered()) {
864 if (partialObject == null && rowValue != null) {
865 nestedResultObjects.clear();
866 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
867 }
868 rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
869 } else {
870 rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
871 if (partialObject == null) {
872 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
873 }
874 }
875 }
876 if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
877 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
878 previousRowValue = null;
879 } else if (rowValue != null) {
880 previousRowValue = rowValue;
881 }
882 }
883
884
885
886
887
888 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
889 final String resultMapId = resultMap.getId();
890 Object rowValue = partialObject;
891 if (rowValue != null) {
892 final MetaObject metaObject = configuration.newMetaObject(rowValue);
893 putAncestor(rowValue, resultMapId);
894 applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
895 ancestorObjects.remove(resultMapId);
896 } else {
897 final ResultLoaderMapltLoaderMap.html#ResultLoaderMap">ResultLoaderMap lazyLoader = new ResultLoaderMap();
898 rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
899 if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
900 final MetaObject metaObject = configuration.newMetaObject(rowValue);
901 boolean foundValues = this.useConstructorMappings;
902 if (shouldApplyAutomaticMappings(resultMap, true)) {
903 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
904 }
905 foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
906 putAncestor(rowValue, resultMapId);
907 foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
908 ancestorObjects.remove(resultMapId);
909 foundValues = lazyLoader.size() > 0 || foundValues;
910 rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
911 }
912 if (combinedKey != CacheKey.NULL_CACHE_KEY) {
913 nestedResultObjects.put(combinedKey, rowValue);
914 }
915 }
916 return rowValue;
917 }
918
919 private void putAncestor(Object resultObject, String resultMapId) {
920 ancestorObjects.put(resultMapId, resultObject);
921 }
922
923
924
925
926
927 private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
928 boolean foundValues = false;
929 for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
930 final String nestedResultMapId = resultMapping.getNestedResultMapId();
931 if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
932 try {
933 final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
934 final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
935 if (resultMapping.getColumnPrefix() == null) {
936
937
938 Object ancestorObject = ancestorObjects.get(nestedResultMapId);
939 if (ancestorObject != null) {
940 if (newObject) {
941 linkObjects(metaObject, resultMapping, ancestorObject);
942 }
943 continue;
944 }
945 }
946 final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
947 final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
948 Object rowValue = nestedResultObjects.get(combinedKey);
949 boolean knownValue = rowValue != null;
950 instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
951 if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
952 rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
953 if (rowValue != null && !knownValue) {
954 linkObjects(metaObject, resultMapping, rowValue);
955 foundValues = true;
956 }
957 }
958 } catch (SQLException e) {
959 throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e);
960 }
961 }
962 }
963 return foundValues;
964 }
965
966 private String getColumnPrefix(String parentPrefix, ResultMapping resultMapping) {
967 final StringBuilder columnPrefixBuilder = new StringBuilder();
968 if (parentPrefix != null) {
969 columnPrefixBuilder.append(parentPrefix);
970 }
971 if (resultMapping.getColumnPrefix() != null) {
972 columnPrefixBuilder.append(resultMapping.getColumnPrefix());
973 }
974 return columnPrefixBuilder.length() == 0 ? null : columnPrefixBuilder.toString().toUpperCase(Locale.ENGLISH);
975 }
976
977 private boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSetWrapper rsw) throws SQLException {
978 Set<String> notNullColumns = resultMapping.getNotNullColumns();
979 if (notNullColumns != null && !notNullColumns.isEmpty()) {
980 ResultSet rs = rsw.getResultSet();
981 for (String column : notNullColumns) {
982 rs.getObject(prependPrefix(column, columnPrefix));
983 if (!rs.wasNull()) {
984 return true;
985 }
986 }
987 return false;
988 } else if (columnPrefix != null) {
989 for (String columnName : rsw.getColumnNames()) {
990 if (columnName.toUpperCase().startsWith(columnPrefix.toUpperCase())) {
991 return true;
992 }
993 }
994 return false;
995 }
996 return true;
997 }
998
999 private ResultMap getNestedResultMap(ResultSet rs, String nestedResultMapId, String columnPrefix) throws SQLException {
1000 ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId);
1001 return resolveDiscriminatedResultMap(rs, nestedResultMap, columnPrefix);
1002 }
1003
1004
1005
1006
1007
1008 private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {
1009 final CacheKeyKey.html#CacheKey">CacheKey cacheKey = new CacheKey();
1010 cacheKey.update(resultMap.getId());
1011 List<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);
1012 if (resultMappings.isEmpty()) {
1013 if (Map.class.isAssignableFrom(resultMap.getType())) {
1014 createRowKeyForMap(rsw, cacheKey);
1015 } else {
1016 createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
1017 }
1018 } else {
1019 createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
1020 }
1021 if (cacheKey.getUpdateCount() < 2) {
1022 return CacheKey.NULL_CACHE_KEY;
1023 }
1024 return cacheKey;
1025 }
1026
1027 private CacheKey../../../../../org/apache/ibatis/cache/CacheKey.html#CacheKey">CacheKey./../../../org/apache/ibatis/cache/CacheKey.html#CacheKey">CacheKey combineKeys(CacheKey../../../../../org/apache/ibatis/cache/CacheKey.html#CacheKey">CacheKey rowKey, CacheKey parentRowKey) {
1028 if (rowKey.getUpdateCount() > 1 && parentRowKey.getUpdateCount() > 1) {
1029 CacheKey combinedKey;
1030 try {
1031 combinedKey = rowKey.clone();
1032 } catch (CloneNotSupportedException e) {
1033 throw new ExecutorException("Error cloning cache key. Cause: " + e, e);
1034 }
1035 combinedKey.update(parentRowKey);
1036 return combinedKey;
1037 }
1038 return CacheKey.NULL_CACHE_KEY;
1039 }
1040
1041 private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {
1042 List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
1043 if (resultMappings.isEmpty()) {
1044 resultMappings = resultMap.getPropertyResultMappings();
1045 }
1046 return resultMappings;
1047 }
1048
1049 private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
1050 for (ResultMapping resultMapping : resultMappings) {
1051 if (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null) {
1052
1053 final ResultMap nestedResultMap = configuration.getResultMap(resultMapping.getNestedResultMapId());
1054 createRowKeyForMappedProperties(nestedResultMap, rsw, cacheKey, nestedResultMap.getConstructorResultMappings(),
1055 prependPrefix(resultMapping.getColumnPrefix(), columnPrefix));
1056 } else if (resultMapping.getNestedQueryId() == null) {
1057 final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);
1058 final TypeHandler<?> th = resultMapping.getTypeHandler();
1059 List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
1060
1061 if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
1062 final Object value = th.getResult(rsw.getResultSet(), column);
1063 if (value != null || configuration.isReturnInstanceForEmptyRow()) {
1064 cacheKey.update(column);
1065 cacheKey.update(value);
1066 }
1067 }
1068 }
1069 }
1070 }
1071
1072 private void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException {
1073 final MetaClass metaType = MetaClass.forClass(resultMap.getType(), reflectorFactory);
1074 List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
1075 for (String column : unmappedColumnNames) {
1076 String property = column;
1077 if (columnPrefix != null && !columnPrefix.isEmpty()) {
1078
1079 if (column.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
1080 property = column.substring(columnPrefix.length());
1081 } else {
1082 continue;
1083 }
1084 }
1085 if (metaType.findProperty(property, configuration.isMapUnderscoreToCamelCase()) != null) {
1086 String value = rsw.getResultSet().getString(column);
1087 if (value != null) {
1088 cacheKey.update(column);
1089 cacheKey.update(value);
1090 }
1091 }
1092 }
1093 }
1094
1095 private void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws SQLException {
1096 List<String> columnNames = rsw.getColumnNames();
1097 for (String columnName : columnNames) {
1098 final String value = rsw.getResultSet().getString(columnName);
1099 if (value != null) {
1100 cacheKey.update(columnName);
1101 cacheKey.update(value);
1102 }
1103 }
1104 }
1105
1106 private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
1107 final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
1108 if (collectionProperty != null) {
1109 final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
1110 targetMetaObject.add(rowValue);
1111 } else {
1112 metaObject.setValue(resultMapping.getProperty(), rowValue);
1113 }
1114 }
1115
1116 private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
1117 final String propertyName = resultMapping.getProperty();
1118 Object propertyValue = metaObject.getValue(propertyName);
1119 if (propertyValue == null) {
1120 Class<?> type = resultMapping.getJavaType();
1121 if (type == null) {
1122 type = metaObject.getSetterType(propertyName);
1123 }
1124 try {
1125 if (objectFactory.isCollection(type)) {
1126 propertyValue = objectFactory.create(type);
1127 metaObject.setValue(propertyName, propertyValue);
1128 return propertyValue;
1129 }
1130 } catch (Exception e) {
1131 throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'. Cause: " + e, e);
1132 }
1133 } else if (objectFactory.isCollection(propertyValue.getClass())) {
1134 return propertyValue;
1135 }
1136 return null;
1137 }
1138
1139 private boolean hasTypeHandlerForResultObject(ResultSetWrapper rsw, Class<?> resultType) {
1140 if (rsw.getColumnNames().size() == 1) {
1141 return typeHandlerRegistry.hasTypeHandler(resultType, rsw.getJdbcType(rsw.getColumnNames().get(0)));
1142 }
1143 return typeHandlerRegistry.hasTypeHandler(resultType);
1144 }
1145
1146 }