Skip to content
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

Added selectMapWithList to handle non-unique column as a key #3239

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 @@ -15,6 +15,7 @@
*/
package org.apache.ibatis.executor.result;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.reflection.MetaObject;
Expand All @@ -30,6 +31,7 @@
public class DefaultMapResultHandler<K, V> implements ResultHandler<V> {

private final Map<K, V> mappedResults;
private final Map<K, List<V>> mappedResultsWithList;
private final String mapKey;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
Expand All @@ -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;
}

Expand All @@ -52,9 +55,14 @@ public void handleResult(ResultContext<? extends V> 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<K, V> getMappedResults() {
return mappedResults;
}

public Map<K, List<V>> getMappedResultsWithList() {
return mappedResultsWithList;
}
}
57 changes: 57 additions & 0 deletions src/main/java/org/apache/ibatis/session/SqlSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,63 @@ public interface SqlSession extends Closeable {
*/
<K, V> Map<K, V> 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<Author>] for selectMap("selectAuthors","id")
*
* @param <K>
* the returned Map keys type
* @param <V>
* 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.
*/
<K, V> Map<K, List<V>> 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 <K>
* the returned Map keys type
* @param <V>
* 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.
*/
<K, V> Map<K, List<V>> 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 <K>
* the returned Map keys type
* @param <V>
* 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.
*/
<K, V> Map<K, List<V>> 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.
*
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/apache/ibatis/session/SqlSessionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,21 @@ public <K, V> Map<K, V> selectMap(String statement, Object parameter, String map
return sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
}

@Override
public <K, V> Map<K, List<V>> selectMapWithList(String statement, String mapKey) {
return sqlSessionProxy.selectMapWithList(statement, mapKey);
}

@Override
public <K, V> Map<K, List<V>> selectMapWithList(String statement, Object parameter, String mapKey) {
return sqlSessionProxy.selectMapWithList(statement, parameter, mapKey);
}

@Override
public <K, V> Map<K, List<V>> selectMapWithList(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return sqlSessionProxy.selectMapWithList(statement, parameter, mapKey, rowBounds);
}

@Override
public <T> Cursor<T> selectCursor(String statement) {
return sqlSessionProxy.selectCursor(statement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,34 @@ public <K, V> Map<K, V> selectMap(String statement, Object parameter, String map

@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return (Map<K, V>) executeSelectMap(statement, parameter, mapKey, rowBounds, false);
}

@Override
public <K, V> Map<K, List<V>> selectMapWithList(String statement, String mapKey) {
return this.selectMapWithList(statement, null, mapKey, RowBounds.DEFAULT);
}

@Override
public <K, V> Map<K, List<V>> selectMapWithList(String statement, Object parameter, String mapKey) {
return this.selectMapWithList(statement, parameter, mapKey, RowBounds.DEFAULT);
}

@Override
public <K, V> Map<K, List<V>> selectMapWithList(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return (Map<K, List<V>>) executeSelectMap(statement, parameter, mapKey, rowBounds, true);
}

private <K, V> Map<K, ?> executeSelectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds, boolean useList) {
final List<? extends V> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
return useList ? mapResultHandler.getMappedResultsWithList() : mapResultHandler.getMappedResults();
}

@Override
Expand Down
20 changes: 20 additions & 0 deletions src/test/java/org/apache/ibatis/session/SqlSessionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,26 @@ void shouldSelectAllAuthorsAsMap() {
}
}

@Test
void shouldSelectAllAuthorsAsMapWithList() {
try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
Author author = new Author(103, "jim", "******", "[email protected]", "Something...", null);
session.insert("org.apache.ibatis.domain.blog.mappers.AuthorMapper.insertAuthor", author);

final Map<String, List<Author>> 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()) {
Expand Down