Skip to content

Commit a3e368d

Browse files
committed
New fix for #22. Better throw an exception to ensure the user knows what
is doing and enables it explicitly.
1 parent ef2d0f5 commit a3e368d

File tree

5 files changed

+37
-17
lines changed

5 files changed

+37
-17
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ private XMLConfigBuilder(XPathParser parser, String environment, Properties prop
8383

8484
public Configuration parse() {
8585
if (parsed) {
86-
throw new BuilderException("Each MapperConfigParser can only be used once.");
86+
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
8787
}
8888
parsed = true;
8989
parseConfiguration(parser.evalNode("/configuration"));
@@ -208,6 +208,7 @@ private void settingsElement(XNode context) throws Exception {
208208
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
209209
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
210210
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
211+
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
211212
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
212213
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
213214
configuration.setLogPrefix(props.getProperty("logPrefix"));

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

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import java.sql.ResultSet;
1919
import java.sql.SQLException;
20-
import java.util.ArrayList;
2120
import java.util.HashMap;
2221
import java.util.List;
2322
import java.util.Locale;
@@ -45,7 +44,6 @@ public class NestedResultSetHandler extends FastResultSetHandler {
4544

4645
private final Map<CacheKey, Object> objectCache = new HashMap<CacheKey, Object>();
4746
private final Map<CacheKey, Object> ancestorCache = new HashMap<CacheKey, Object>();
48-
private final List<Object> unsortedRowHandlerElements = new ArrayList<Object>();
4947

5048
public NestedResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql, RowBounds rowBounds) {
5149
super(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
@@ -65,11 +63,20 @@ private void ensureNoRowBounds(RowBounds rowBounds) {
6563
// HANDLE RESULT SETS
6664
//
6765

66+
@Override
67+
protected void handleResultSet(ResultSet rs, ResultMap resultMap, List<Object> multipleResults, ResultColumnCache resultColumnCache) throws SQLException {
68+
if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered()) {
69+
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
70+
+ "Use safeResultHandlerEnabled=false setting to bypass this check "
71+
+ "or ensure your statement returns ordered data and set resultOrdered=true on it.");
72+
}
73+
super.handleResultSet(rs, resultMap, multipleResults, resultColumnCache);
74+
}
75+
6876
@Override
6977
protected void cleanUpAfterHandlingResultSet() {
7078
super.cleanUpAfterHandlingResultSet();
7179
objectCache.clear();
72-
unsortedRowHandlerElements.clear();
7380
}
7481

7582
//
@@ -85,25 +92,21 @@ protected void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler
8592
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);
8693
final CacheKey rowKey = createRowKey(discriminatedResultMap, rs, null, resultColumnCache);
8794
Object partialObject = objectCache.get(rowKey);
88-
if (mappedStatement.isResultOrdered()) { // issue #577
95+
if (mappedStatement.isResultOrdered()) { // issue #577 && #542
8996
if (partialObject == null && rowValue != null) {
90-
objectCache.clear();
97+
objectCache.clear();
9198
callResultHandler(resultHandler, resultContext, rowValue);
92-
}
99+
}
93100
rowValue = getRowValue(rs, discriminatedResultMap, rowKey, rowKey, null, resultColumnCache, partialObject);
94101
} else {
95102
rowValue = getRowValue(rs, discriminatedResultMap, rowKey, rowKey, null, resultColumnCache, partialObject);
96103
if (partialObject == null) {
97-
unsortedRowHandlerElements.add(rowValue); // issue #542
104+
callResultHandler(resultHandler, resultContext, rowValue);
98105
}
99106
}
100107
}
101-
if (mappedStatement.isResultOrdered()) {
102-
if (rowValue != null) callResultHandler(resultHandler, resultContext, rowValue);
103-
} else {
104-
for (Object o : unsortedRowHandlerElements) {
105-
callResultHandler(resultHandler, resultContext, o);
106-
}
108+
if (rowValue != null && mappedStatement.isResultOrdered()) {
109+
callResultHandler(resultHandler, resultContext, rowValue);
107110
}
108111
}
109112

src/main/java/org/apache/ibatis/session/Configuration.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,10 @@ public void setDatabaseId(String databaseId) {
218218
this.databaseId = databaseId;
219219
}
220220

221-
@Deprecated
222221
public boolean isSafeResultHandlerEnabled() {
223222
return safeResultHandlerEnabled;
224223
}
225224

226-
@Deprecated // "Not needed as of the fix for issue #542"
227225
public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
228226
this.safeResultHandlerEnabled = safeResultHandlerEnabled;
229227
}

src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</collection>
2929
</resultMap>
3030

31-
<select id="getPersons" resultMap="personResult">
31+
<select id="getPersons" resultMap="personResult" resultOrdered="true">
3232
select p.id as person_id, p.name as person_name, i.id as item_id, i.name as item_name
3333
from persons p, items i
3434
where p.id = i.owner

src/test/java/org/apache/ibatis/submitted/nestedresulthandler/NestedResultHandlerTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.sql.Connection;
2020
import java.util.List;
2121

22+
import org.apache.ibatis.exceptions.PersistenceException;
2223
import org.apache.ibatis.io.Resources;
2324
import org.apache.ibatis.jdbc.ScriptRunner;
2425
import org.apache.ibatis.session.ResultContext;
@@ -98,6 +99,23 @@ public void handleResult(ResultContext context) {
9899
}
99100
}
100101

102+
@Test(expected=PersistenceException.class)
103+
public void testUnorderedGetPersonWithHandler() {
104+
SqlSession sqlSession = sqlSessionFactory.openSession();
105+
try {
106+
sqlSession.select("getPersonsWithItemsOrdered", new ResultHandler() {
107+
public void handleResult(ResultContext context) {
108+
Person person = (Person) context.getResultObject();
109+
if ("grandma".equals(person.getName())) {
110+
Assert.assertEquals(2, person.getItems().size());
111+
}
112+
}
113+
});
114+
} finally {
115+
sqlSession.close();
116+
}
117+
}
118+
101119
/**
102120
* Fix bug caused by issue #542, see new issue #22 on github If we order by a
103121
* nested result map attribute we can miss some records and end up with

0 commit comments

Comments
 (0)