diff --git a/src/main/java/org/apache/ibatis/executor/result/DefaultMapResultHandler.java b/src/main/java/org/apache/ibatis/executor/result/DefaultMapResultHandler.java index f2ee92bcc02..f4171d35f8d 100644 --- a/src/main/java/org/apache/ibatis/executor/result/DefaultMapResultHandler.java +++ b/src/main/java/org/apache/ibatis/executor/result/DefaultMapResultHandler.java @@ -15,6 +15,7 @@ */ package org.apache.ibatis.executor.result; +import java.util.List; import java.util.Map; import org.apache.ibatis.reflection.MetaObject; @@ -30,6 +31,7 @@ public class DefaultMapResultHandler implements ResultHandler { private final Map mappedResults; + private final Map> mappedResultsWithList; private final String mapKey; private final ObjectFactory objectFactory; private final ObjectWrapperFactory objectWrapperFactory; @@ -42,6 +44,7 @@ public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, Objec this.objectWrapperFactory = objectWrapperFactory; this.reflectorFactory = reflectorFactory; this.mappedResults = objectFactory.create(Map.class); + this.mappedResultsWithList = objectFactory.create(Map.class); this.mapKey = mapKey; } @@ -52,9 +55,14 @@ public void handleResult(ResultContext context) { // TODO is that assignment always true? final K key = (K) mo.getValue(mapKey); mappedResults.put(key, value); + mappedResultsWithList.computeIfAbsent(key, k -> objectFactory.create(List.class)).add(value); } public Map getMappedResults() { return mappedResults; } + + public Map> getMappedResultsWithList() { + return mappedResultsWithList; + } } diff --git a/src/main/java/org/apache/ibatis/session/SqlSession.java b/src/main/java/org/apache/ibatis/session/SqlSession.java index bb0f85932dc..6faa01f6150 100644 --- a/src/main/java/org/apache/ibatis/session/SqlSession.java +++ b/src/main/java/org/apache/ibatis/session/SqlSession.java @@ -156,6 +156,63 @@ public interface SqlSession extends Closeable { */ Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); + /** + * The selectMapWithList is a special case in that it is designed to convert a list of results into a Map based on one of the + * properties not needed to be unique in the resulting objects. Eg. Return a of Map[Integer,List] for selectMap("selectAuthors","id") + * + * @param + * the returned Map keys type + * @param + * the returned Map values type + * @param statement + * Unique identifier matching the statement to use. + * @param mapKey + * The property to use as key for each value in the list. + * + * @return Map containing key pair data. + */ + Map> selectMapWithList(String statement, String mapKey); + + /** + * The selectMap is a special case in that it is designed to convert a list of results into a Map based on one of the + * properties not needed to be unique in the resulting objects. + * + * @param + * the returned Map keys type + * @param + * the returned Map values type + * @param statement + * Unique identifier matching the statement to use. + * @param parameter + * A parameter object to pass to the statement. + * @param mapKey + * The property to use as key for each value in the list. + * + * @return Map containing key pair data. + */ + Map> selectMapWithList(String statement, Object parameter, String mapKey); + + /** + * The selectMap is a special case in that it is designed to convert a list of results into a Map based on one of the + * properties not needed to be unique in the resulting objects. + * + * @param + * the returned Map keys type + * @param + * the returned Map values type + * @param statement + * Unique identifier matching the statement to use. + * @param parameter + * A parameter object to pass to the statement. + * @param mapKey + * The property to use as key for each value in the list. + * @param rowBounds + * Bounds to limit object retrieval + * + * @return Map containing key pair data. + */ + Map> selectMapWithList(String statement, Object parameter, String mapKey, RowBounds rowBounds); + /** * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator. * diff --git a/src/main/java/org/apache/ibatis/session/SqlSessionManager.java b/src/main/java/org/apache/ibatis/session/SqlSessionManager.java index 183aa878f9c..2860ba34eba 100644 --- a/src/main/java/org/apache/ibatis/session/SqlSessionManager.java +++ b/src/main/java/org/apache/ibatis/session/SqlSessionManager.java @@ -179,6 +179,21 @@ public Map selectMap(String statement, Object parameter, String map return sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds); } + @Override + public Map> selectMapWithList(String statement, String mapKey) { + return sqlSessionProxy.selectMapWithList(statement, mapKey); + } + + @Override + public Map> selectMapWithList(String statement, Object parameter, String mapKey) { + return sqlSessionProxy.selectMapWithList(statement, parameter, mapKey); + } + + @Override + public Map> selectMapWithList(String statement, Object parameter, String mapKey, RowBounds rowBounds) { + return sqlSessionProxy.selectMapWithList(statement, parameter, mapKey, rowBounds); + } + @Override public Cursor selectCursor(String statement) { return sqlSessionProxy.selectCursor(statement); diff --git a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java index 62b39becea5..32e974ccd6a 100644 --- a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java +++ b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java @@ -96,15 +96,34 @@ public Map selectMap(String statement, Object parameter, String map @Override public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { + return (Map) executeSelectMap(statement, parameter, mapKey, rowBounds, false); + } + + @Override + public Map> selectMapWithList(String statement, String mapKey) { + return this.selectMapWithList(statement, null, mapKey, RowBounds.DEFAULT); + } + + @Override + public Map> selectMapWithList(String statement, Object parameter, String mapKey) { + return this.selectMapWithList(statement, parameter, mapKey, RowBounds.DEFAULT); + } + + @Override + public Map> selectMapWithList(String statement, Object parameter, String mapKey, RowBounds rowBounds) { + return (Map>) executeSelectMap(statement, parameter, mapKey, rowBounds, true); + } + + private Map executeSelectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds, boolean useList) { final List list = selectList(statement, parameter, rowBounds); final DefaultMapResultHandler mapResultHandler = new DefaultMapResultHandler<>(mapKey, - configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); + configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); final DefaultResultContext context = new DefaultResultContext<>(); for (V o : list) { context.nextResultObject(o); mapResultHandler.handleResult(context); } - return mapResultHandler.getMappedResults(); + return useList ? mapResultHandler.getMappedResultsWithList() : mapResultHandler.getMappedResults(); } @Override diff --git a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java index 0b72e1d385e..65778709499 100644 --- a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java +++ b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java @@ -180,6 +180,26 @@ void shouldSelectAllAuthorsAsMap() { } } + @Test + void shouldSelectAllAuthorsAsMapWithList() { + try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) { + Author author = new Author(103, "jim", "******", "jim@somewhere.com", "Something...", null); + session.insert("org.apache.ibatis.domain.blog.mappers.AuthorMapper.insertAuthor", author); + + final Map> authors = session + .selectMapWithList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors", "username"); + authors.forEach((k, v) -> { + if (k.equals("jim")) { + assertEquals(2, v.size()); + v.forEach(a -> assertEquals("jim", a.getUsername())); + } else if (k.equals("sally")) { + assertEquals(1, v.size()); + v.forEach(a -> assertEquals("sally", a.getUsername())); + } + }); + } + } + @Test void shouldSelectCountOfPosts() { try (SqlSession session = sqlMapper.openSession()) {