Skip to content

Commit 2107698

Browse files
committed
Fixes ##149. Selective Lazy Loading.
1 parent 074cef5 commit 2107698

File tree

10 files changed

+84
-66
lines changed

10 files changed

+84
-66
lines changed

src/main/java/org/apache/ibatis/annotations/Many.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2011 the original author or authors.
2+
* Copyright 2009-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,8 +20,13 @@
2020
import java.lang.annotation.RetentionPolicy;
2121
import java.lang.annotation.Target;
2222

23+
import org.apache.ibatis.session.Loading;
24+
2325
@Retention(RetentionPolicy.RUNTIME)
2426
@Target(ElementType.METHOD)
2527
public @interface Many {
2628
String select() default "";
29+
30+
Loading lazy() default Loading.DEFAULT;
31+
2732
}

src/main/java/org/apache/ibatis/annotations/One.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2011 the original author or authors.
2+
* Copyright 2009-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,8 +20,13 @@
2020
import java.lang.annotation.RetentionPolicy;
2121
import java.lang.annotation.Target;
2222

23+
import org.apache.ibatis.session.Loading;
24+
2325
@Retention(RetentionPolicy.RUNTIME)
2426
@Target(ElementType.METHOD)
2527
public @interface One {
2628
String select() default "";
29+
30+
Loading lazy() default Loading.DEFAULT;
31+
2732
}

src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -205,45 +205,14 @@ public ResultMap addResultMap(
205205
return resultMap;
206206
}
207207

208-
public ResultMapping buildResultMapping(
209-
Class<?> resultType,
210-
String property,
211-
String column,
212-
Class<?> javaType,
213-
JdbcType jdbcType,
214-
String nestedSelect,
215-
String nestedResultMap,
216-
String notNullColumn,
217-
String columnPrefix,
218-
Class<? extends TypeHandler<?>> typeHandler,
219-
List<ResultFlag> flags,
220-
String resultSet,
221-
String foreignColumn) {
222-
ResultMapping resultMapping = assembleResultMapping(
223-
resultType,
224-
property,
225-
column,
226-
javaType,
227-
jdbcType,
228-
nestedSelect,
229-
nestedResultMap,
230-
notNullColumn,
231-
columnPrefix,
232-
typeHandler,
233-
flags,
234-
resultSet,
235-
foreignColumn);
236-
return resultMapping;
237-
}
238-
239208
public Discriminator buildDiscriminator(
240209
Class<?> resultType,
241210
String column,
242211
Class<?> javaType,
243212
JdbcType jdbcType,
244213
Class<? extends TypeHandler<?>> typeHandler,
245214
Map<String, String> discriminatorMap) {
246-
ResultMapping resultMapping = assembleResultMapping(
215+
ResultMapping resultMapping = buildResultMapping(
247216
resultType,
248217
null,
249218
column,
@@ -256,7 +225,8 @@ public Discriminator buildDiscriminator(
256225
typeHandler,
257226
new ArrayList<ResultFlag>(),
258227
null,
259-
null);
228+
null,
229+
false);
260230
Map<String, String> namespaceDiscriminatorMap = new HashMap<String, String>();
261231
for (Map.Entry<String, String> e : discriminatorMap.entrySet()) {
262232
String resultMap = e.getValue();
@@ -394,7 +364,7 @@ private void setStatementTimeout(Integer timeout, MappedStatement.Builder statem
394364
statementBuilder.timeout(timeout);
395365
}
396366

397-
private ResultMapping assembleResultMapping(
367+
public ResultMapping buildResultMapping(
398368
Class<?> resultType,
399369
String property,
400370
String column,
@@ -407,7 +377,8 @@ private ResultMapping assembleResultMapping(
407377
Class<? extends TypeHandler<?>> typeHandler,
408378
List<ResultFlag> flags,
409379
String resultSet,
410-
String foreignColumn) {
380+
String foreignColumn,
381+
boolean lazy) {
411382
Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
412383
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
413384
List<ResultMapping> composites = parseCompositeColumnName(column);
@@ -423,6 +394,7 @@ private ResultMapping assembleResultMapping(
423394
builder.notNullColumns(parseMultipleColumnNames(notNullColumn));
424395
builder.columnPrefix(columnPrefix);
425396
builder.foreignColumn(foreignColumn);
397+
builder.lazy(lazy);
426398
return builder.build();
427399
}
428400

@@ -503,7 +475,7 @@ public ResultMapping buildResultMapping(
503475
List<ResultFlag> flags) {
504476
return buildResultMapping(
505477
resultType, property, column, javaType, jdbcType, nestedSelect,
506-
nestedResultMap, notNullColumn, columnPrefix, typeHandler, flags, null, null);
478+
nestedResultMap, notNullColumn, columnPrefix, typeHandler, flags, null, null, configuration.isLazyLoadingEnabled());
507479
}
508480

509481
public LanguageDriver getLanguageDriver(Class<?> langClass) {

src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import org.apache.ibatis.mapping.StatementType;
7777
import org.apache.ibatis.scripting.LanguageDriver;
7878
import org.apache.ibatis.session.Configuration;
79+
import org.apache.ibatis.session.Loading;
7980
import org.apache.ibatis.session.ResultHandler;
8081
import org.apache.ibatis.session.RowBounds;
8182
import org.apache.ibatis.type.JdbcType;
@@ -477,7 +478,8 @@ private void applyResults(Result[] results, Class<?> resultType, List<ResultMapp
477478
result.typeHandler() == UnknownTypeHandler.class ? null : result.typeHandler(),
478479
flags,
479480
null,
480-
null);
481+
null,
482+
isLazy(result));
481483
resultMappings.add(resultMapping);
482484
}
483485
}
@@ -493,8 +495,32 @@ private String nestedSelectId(Result result) {
493495
return nestedSelect;
494496
}
495497

498+
private boolean isLazy(Result result) {
499+
Boolean isLazy = null;
500+
if (Loading.DEFAULT != result.one().lazy()) {
501+
isLazy = (result.one().lazy() == Loading.LAZY);
502+
}
503+
if (Loading.DEFAULT != result.many().lazy()) {
504+
if (isLazy == null) {
505+
isLazy = (result.many().lazy() == Loading.LAZY);
506+
} else {
507+
throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
508+
}
509+
}
510+
if (isLazy == null) {
511+
isLazy = configuration.isLazyLoadingEnabled();
512+
}
513+
return isLazy;
514+
}
515+
496516
private boolean hasNestedSelect(Result result) {
497-
return result.one().select().length() > 0 || result.many().select().length() > 0;
517+
boolean hasNestedSelect = result.one().select().length() > 0;
518+
if (hasNestedSelect) {
519+
throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
520+
} else {
521+
hasNestedSelect = result.many().select().length() > 0;
522+
}
523+
return hasNestedSelect;
498524
}
499525

500526
private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
@@ -515,7 +541,8 @@ private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMa
515541
arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler(),
516542
flags,
517543
null,
518-
null);
544+
null,
545+
false);
519546
resultMappings.add(resultMapping);
520547
}
521548
}

src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,11 +364,12 @@ private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resu
364364
String typeHandler = context.getStringAttribute("typeHandler");
365365
String resulSet = context.getStringAttribute("resultSet");
366366
String foreignColumn = context.getStringAttribute("foreignColumn");
367+
boolean lazy = context.getBooleanAttribute("lazy", configuration.isLazyLoadingEnabled());
367368
Class<?> javaTypeClass = resolveClass(javaType);
368369
@SuppressWarnings("unchecked")
369370
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
370371
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
371-
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn);
372+
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
372373
}
373374

374375
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception {

src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ columnPrefix CDATA #IMPLIED
117117
resultSet CDATA #IMPLIED
118118
foreignColumn CDATA #IMPLIED
119119
autoMapping (true|false) #IMPLIED
120+
lazy (true|false) #IMPLIED
120121
>
121122

122123
<!ELEMENT association (constructor?,id*,result*,association*,collection*, discriminator?)>
@@ -133,6 +134,7 @@ columnPrefix CDATA #IMPLIED
133134
resultSet CDATA #IMPLIED
134135
foreignColumn CDATA #IMPLIED
135136
autoMapping (true|false) #IMPLIED
137+
lazy (true|false) #IMPLIED
136138
>
137139

138140
<!ELEMENT discriminator (case+)>

src/main/java/org/apache/ibatis/executor/BaseExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowB
272272
protected Connection getConnection(Log statementLog) throws SQLException {
273273
Connection connection = transaction.getConnection();
274274
if (statementLog.isDebugEnabled()) {
275-
return ConnectionLogger.newInstance(connection, statementLog);
275+
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
276276
} else {
277277
return connection;
278278
}

src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
326326
//
327327

328328
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
329-
final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();
329+
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
330330
Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
331331
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
332332
final MetaObject metaObject = configuration.newMetaObject(resultObject);
@@ -335,7 +335,7 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQL
335335
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
336336
}
337337
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
338-
foundValues = (lazyLoader != null && lazyLoader.size() > 0) || foundValues;
338+
foundValues = lazyLoader.size() > 0 || foundValues;
339339
resultObject = foundValues ? resultObject : null;
340340
return resultObject;
341341
}
@@ -346,10 +346,6 @@ private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean def) {
346346
return resultMap.getAutoMapping() != null ? resultMap.getAutoMapping() : def;
347347
}
348348

349-
private ResultLoaderMap instantiateResultLoaderMap() {
350-
return configuration.isLazyLoadingEnabled() ? new ResultLoaderMap() : null;
351-
}
352-
353349
//
354350
// PROPERTY MAPPINGS
355351
//
@@ -508,10 +504,10 @@ private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, Res
508504
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
509505
final List<Object> constructorArgs = new ArrayList<Object>();
510506
final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
511-
if (resultObject != null && configuration.isLazyLoadingEnabled() && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
507+
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
512508
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
513509
for (ResultMapping propertyMapping : propertyMappings) {
514-
if (propertyMapping.getNestedQueryId() != null) { // issue #109 (avoid creating proxies for leaf objects)
510+
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149
515511
return proxyFactory.createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
516512
}
517513
}
@@ -605,7 +601,7 @@ private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObj
605601
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
606602
} else {
607603
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
608-
if (configuration.isLazyLoadingEnabled()) {
604+
if (propertyMapping.isLazy()) {
609605
lazyLoader.addLoader(property, metaResultObject, resultLoader);
610606
} else {
611607
value = resultLoader.loadResult();
@@ -737,7 +733,7 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey c
737733
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
738734
ancestorObjects.remove(absoluteKey);
739735
} else {
740-
final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();
736+
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
741737
resultObject = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
742738
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
743739
final MetaObject metaObject = configuration.newMetaObject(resultObject);
@@ -749,7 +745,7 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey c
749745
putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
750746
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
751747
ancestorObjects.remove(absoluteKey);
752-
foundValues = (lazyLoader != null && lazyLoader.size() > 0) || foundValues;
748+
foundValues = lazyLoader.size() > 0 || foundValues;
753749
resultObject = foundValues ? resultObject : null;
754750
}
755751
if (combinedKey != CacheKey.NULL_CACHE_KEY) nestedResultObjects.put(combinedKey, resultObject);

src/main/java/org/apache/ibatis/mapping/ResultMapping.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2013 the original author or authors.
2+
* Copyright 2009-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@ public class ResultMapping {
4141
private List<ResultMapping> composites;
4242
private String resultSet;
4343
private String foreignColumn;
44+
private boolean lazy;
4445

4546
private ResultMapping() {
4647
}
@@ -49,28 +50,23 @@ public static class Builder {
4950
private ResultMapping resultMapping = new ResultMapping();
5051

5152
public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
52-
resultMapping.configuration = configuration;
53-
resultMapping.property = property;
53+
this(configuration, property);
5454
resultMapping.column = column;
5555
resultMapping.typeHandler = typeHandler;
56-
resultMapping.flags = new ArrayList<ResultFlag>();
57-
resultMapping.composites = new ArrayList<ResultMapping>();
5856
}
5957

6058
public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
61-
resultMapping.configuration = configuration;
62-
resultMapping.property = property;
59+
this(configuration, property);
6360
resultMapping.column = column;
6461
resultMapping.javaType = javaType;
65-
resultMapping.flags = new ArrayList<ResultFlag>();
66-
resultMapping.composites = new ArrayList<ResultMapping>();
6762
}
6863

6964
public Builder(Configuration configuration, String property) {
7065
resultMapping.configuration = configuration;
7166
resultMapping.property = property;
7267
resultMapping.flags = new ArrayList<ResultFlag>();
7368
resultMapping.composites = new ArrayList<ResultMapping>();
69+
resultMapping.lazy = configuration.isLazyLoadingEnabled();
7470
}
7571

7672
public Builder javaType(Class<?> javaType) {
@@ -128,6 +124,11 @@ public Builder composites(List<ResultMapping> composites) {
128124
return this;
129125
}
130126

127+
public Builder lazy(boolean lazy) {
128+
resultMapping.lazy = lazy;
129+
return this;
130+
}
131+
131132
public ResultMapping build() {
132133
// lock down collections
133134
resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
@@ -239,6 +240,14 @@ public void setForeignColumn(String foreignColumn) {
239240
this.foreignColumn = foreignColumn;
240241
}
241242

243+
public boolean isLazy() {
244+
return lazy;
245+
}
246+
247+
public void setLazy(boolean lazy) {
248+
this.lazy = lazy;
249+
}
250+
242251
@Override
243252
public boolean equals(Object o) {
244253
if (this == o) {
@@ -267,4 +276,5 @@ public int hashCode() {
267276
return 0;
268277
}
269278
}
279+
270280
}

0 commit comments

Comments
 (0)