Skip to content

Missing and duplicate results #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand All @@ -42,6 +45,8 @@

public class NestedResultSetHandler extends FastResultSetHandler {

private List<Tuple> unhandledObjects = new ArrayList<Tuple>();
private Set<CacheKey> handledObjects = new HashSet<CacheKey>();
private final Map<CacheKey, Object> objectCache = new HashMap<CacheKey, Object>();
private final Map<CacheKey, Object> ancestorCache = new HashMap<CacheKey, Object>();

Expand Down Expand Up @@ -83,14 +88,35 @@ protected void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler
final CacheKey rowKey = createRowKey(discriminatedResultMap, rs, null, resultColumnCache);
Object partialObject = objectCache.get(rowKey);
if (partialObject == null && rowValue != null) { // issue #542 delay calling ResultHandler until object ends
if (mappedStatement.isResultOrdered()) objectCache.clear(); // issue #577 clear memory if ordered
if (mappedStatement.isResultOrdered()) clearCache(); // issue #577 clear memory if ordered
callResultHandler(resultHandler, resultContext, rowValue);
}
rowValue = getRowValue(rs, discriminatedResultMap, rowKey, rowKey, null, resultColumnCache, partialObject);
if( partialObject == null ) {
// issue #542 when results are not ordered (as described in comment #1), need to keep track of what was or wasn't handled
unhandledObjects.add(new Tuple(rowKey, rowValue));
}
}
if (rowValue != null) callResultHandler(resultHandler, resultContext, rowValue);
}


@Override
protected void callResultHandler(ResultHandler resultHandler, DefaultResultContext resultContext, Object rowValueIgnored) {
for( Iterator<Tuple> iter = unhandledObjects.iterator(); iter.hasNext(); ) {
Tuple tuple = iter.next();
if( !handledObjects.contains(tuple.getRowKey()) ) {
super.callResultHandler(resultHandler, resultContext, tuple.getRowValue());
handledObjects.add(tuple.getRowKey());
}
}
unhandledObjects.clear();
}

private void clearCache() {
handledObjects.clear();
objectCache.clear();
}

//
// GET VALUE FROM ROW
//
Expand Down Expand Up @@ -324,4 +350,19 @@ private void createRowKeyForMap(ResultSet rs, CacheKey cacheKey, ResultColumnCac
}
}

private static class Tuple {
private CacheKey rowKey;
private Object rowValue;

public Tuple(CacheKey rowKey, Object rowValue) {
this.rowKey = rowKey;
this.rowValue = rowValue;
}
public CacheKey getRowKey() {
return rowKey;
}
public Object getRowValue() {
return rowValue;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@

public interface Mapper {
List<Person> getPersons();
List<Person> getPersonsWithItemsOrdered();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,15 @@
</resultMap>

<select id="getPersons" resultMap="personResult">
select p.id as person_id, p.name as person_name, i.id as item_id, i.name as item_name
from persons p, items i
select p.id as person_id, p.name as person_name, i.id as item_id, i.name as item_name
from persons p, items i
where p.id = i.owner
</select>

<select id="getPersonsWithItemsOrdered" resultMap="personResult">
select p.id as person_id, p.name as person_name, i.id as item_id, i.name as item_name
from persons p, items i
where p.id = i.owner
order by i.name
</select>
</mapper>
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void testGetPerson() {
Mapper mapper = sqlSession.getMapper(Mapper.class);

List<Person> persons = mapper.getPersons();

Person person = persons.get(0);
Assert.assertEquals("grandma", person.getName());
Assert.assertTrue(person.owns("book"));
Expand All @@ -79,7 +79,7 @@ public void testGetPerson() {
sqlSession.close();
}
}

@Test // issue #542
public void testGetPersonWithHandler() {
SqlSession sqlSession = sqlSessionFactory.openSession();
Expand All @@ -91,10 +91,43 @@ public void handleResult(ResultContext context) {
Assert.assertEquals(2, person.getItems().size());
}
}
});
});
} finally {
sqlSession.close();
}
}

/**
* Fix bug caused by issue #542, see new issue #22 on github
* If we order by a nested result map attribute we can miss some records and end up with duplicates instead.
*/
@Test
public void testGetPersonOrderedByItem() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);

List<Person> persons = mapper.getPersonsWithItemsOrdered();

Person person = persons.get(0);
Assert.assertEquals("grandma", person.getName());
Assert.assertTrue(person.owns("book"));
Assert.assertTrue(person.owns("tv"));
Assert.assertEquals(2, person.getItems().size());

person = persons.get(1);
Assert.assertEquals("brother", person.getName());
Assert.assertTrue(person.owns("car"));
Assert.assertEquals(1, person.getItems().size());

person = persons.get(2);
Assert.assertEquals("sister", person.getName());
Assert.assertTrue(person.owns("phone"));
Assert.assertTrue(person.owns("shoes"));
Assert.assertEquals(2, person.getItems().size());
} finally {
sqlSession.close();
}
}

}