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