diff --git a/jOOQ-test/src/org/jooq/test/BaseTest.java b/jOOQ-test/src/org/jooq/test/BaseTest.java index f66e2b0d8f..b5f967ee47 100644 --- a/jOOQ-test/src/org/jooq/test/BaseTest.java +++ b/jOOQ-test/src/org/jooq/test/BaseTest.java @@ -158,6 +158,7 @@ public abstract class BaseTest< protected static final List BOOK_IDS_SHORT = Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4); protected static final List BOOK_IDS = Arrays.asList(1, 2, 3, 4); + protected static final List BOOK_IDS_STRING = Arrays.asList("1", "2", "3", "4"); protected static final List BOOK_AUTHOR_IDS = Arrays.asList(1, 1, 2, 2); protected static final List BOOK_TITLES = Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida"); protected static final List BOOK_FIRST_NAMES = Arrays.asList("George", "George", "Paulo", "Paulo"); diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java index 194943b5a2..d0f42b233e 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java @@ -2190,6 +2190,92 @@ extends BaseTest bookIdMapper = new RecordMapper() { + @Override + public String map(Record record) { + return record.getValue(TBook_ID(), String.class); + } + }; + RecordMapper authorIdMapper = new RecordMapper() { + @Override + public String map(Record record) { + return record.getValue(TBook_AUTHOR_ID(), String.class); + } + }; + + Map> groups1 = + create().select(TBook_AUTHOR_ID(), TBook_ID()) + .from(TBook()) + .orderBy(TBook_AUTHOR_ID(), TBook_ID()) + .fetchGroups(TBook_AUTHOR_ID(), bookIdMapper); + + assertEquals(asList(1, 2), new ArrayList(groups1.keySet())); + assertEquals(asList("1", "2"), groups1.get(1)); + assertEquals(asList("3", "4"), groups1.get(2)); + + Map> groups2 = + create().select(TBook_AUTHOR_ID(), TBook_ID()) + .from(TBook()) + .orderBy(TBook_AUTHOR_ID(), TBook_ID()) + .fetchGroups(new Field[] { + TBook_ID(), + TBook_AUTHOR_ID() + }, bookIdMapper); + + assertEquals(4, groups2.size()); + assertEquals(1, (int) new ArrayList(groups2.keySet()).get(0).getValue(TBook_ID())); + assertEquals(2, (int) new ArrayList(groups2.keySet()).get(1).getValue(TBook_ID())); + assertEquals(3, (int) new ArrayList(groups2.keySet()).get(2).getValue(TBook_ID())); + assertEquals(4, (int) new ArrayList(groups2.keySet()).get(3).getValue(TBook_ID())); + assertEquals(1, (int) new ArrayList(groups2.keySet()).get(0).getValue(TBook_AUTHOR_ID())); + assertEquals(1, (int) new ArrayList(groups2.keySet()).get(1).getValue(TBook_AUTHOR_ID())); + assertEquals(2, (int) new ArrayList(groups2.keySet()).get(2).getValue(TBook_AUTHOR_ID())); + assertEquals(2, (int) new ArrayList(groups2.keySet()).get(3).getValue(TBook_AUTHOR_ID())); + assertEquals("1", new ArrayList>(groups2.values()).get(0).get(0)); + assertEquals("2", new ArrayList>(groups2.values()).get(1).get(0)); + assertEquals("3", new ArrayList>(groups2.values()).get(2).get(0)); + assertEquals("4", new ArrayList>(groups2.values()).get(3).get(0)); + assertEquals(1, new ArrayList>(groups2.values()).get(0).size()); + assertEquals(1, new ArrayList>(groups2.values()).get(1).size()); + assertEquals(1, new ArrayList>(groups2.values()).get(2).size()); + assertEquals(1, new ArrayList>(groups2.values()).get(3).size()); + + + Map maps1 = + create().select(TBook_AUTHOR_ID(), TBook_ID()) + .from(TBook()) + .orderBy(TBook_AUTHOR_ID(), TBook_ID()) + .fetchMap(TBook_ID(), authorIdMapper); + + assertEquals(asList(1, 2, 3, 4), new ArrayList(maps1.keySet())); + assertEquals(asList("1", "1", "2", "2"), new ArrayList(maps1.values())); + + Map, String> maps2 = + create().select(TBook_AUTHOR_ID(), TBook_ID()) + .from(TBook()) + .orderBy(TBook_AUTHOR_ID(), TBook_ID()) + .fetchMap(new Field[] { + TBook_ID(), + TBook_AUTHOR_ID() + }, bookIdMapper); + + assertEquals(4, maps2.size()); + assertEquals(1, new ArrayList>(maps2.keySet()).get(0).get(0)); + assertEquals(2, new ArrayList>(maps2.keySet()).get(1).get(0)); + assertEquals(3, new ArrayList>(maps2.keySet()).get(2).get(0)); + assertEquals(4, new ArrayList>(maps2.keySet()).get(3).get(0)); + assertEquals(1, new ArrayList>(maps2.keySet()).get(0).get(1)); + assertEquals(1, new ArrayList>(maps2.keySet()).get(1).get(1)); + assertEquals(2, new ArrayList>(maps2.keySet()).get(2).get(1)); + assertEquals(2, new ArrayList>(maps2.keySet()).get(3).get(1)); + assertEquals("1", new ArrayList(maps2.values()).get(0)); + assertEquals("2", new ArrayList(maps2.values()).get(1)); + assertEquals("3", new ArrayList(maps2.values()).get(2)); + assertEquals("4", new ArrayList(maps2.values()).get(3)); + } + @Test public void testFetchWithMaxRows() throws Exception { Result books = diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 127af4061a..38706c1e91 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -1086,6 +1086,11 @@ public abstract class jOOQAbstractTest< new FetchTests(this).testFetchGroupsPOJO(); } + @Test + public void testFetchGroupsMapper() throws Exception { + new FetchTests(this).testFetchGroupsMapper(); + } + @Test public void testFetchWithMaxRows() throws Exception { new FetchTests(this).testFetchWithMaxRows(); diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index cd36f6f7f0..37624abf35 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -537,6 +537,7 @@ public interface Result extends List, Attachable { * * @param key The key. Client code must assure that key is unique in the * result set. + * @param type The entity type. * @return A Map containing the result. * @throws IllegalArgumentException If the argument field is not contained * in {@link #fieldsRow()} @@ -549,6 +550,29 @@ public interface Result extends List, Attachable { Map intoMap(Field key, Class type) throws IllegalArgumentException, InvalidResultException, MappingException; + /** + * Return a {@link Map} with results grouped by the given key and mapped by + * the given mapper. + *

+ * An {@link InvalidResultException} is thrown, if the key is non-unique in + * the result set. Use {@link #intoGroups(Field, Class)} instead, if your + * key is non-unique. + * + * @param key The key. Client code must assure that key is unique in the + * result set. + * @param mapper The mapper callback. + * @return A Map containing the result. + * @throws IllegalArgumentException If the argument field is not contained + * in {@link #fieldsRow()} + * @throws InvalidResultException if the key is non-unique in the result + * set. + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + * @see DefaultRecordMapper + */ + Map intoMap(Field key, RecordMapper mapper) throws IllegalArgumentException, + InvalidResultException, MappingException; + /** * Return a {@link Map} with results grouped by the given keys and mapped * into the given entity type. @@ -560,6 +584,7 @@ public interface Result extends List, Attachable { * @param keys The keys. Client code must assure that keys are unique in the * result set. If this is null or an empty array, * the resulting map will contain at most one entry. + * @param type The entity type. * @return A Map containing the results. * @throws IllegalArgumentException If any of the argument fields is not * contained in {@link #fieldsRow()} @@ -572,6 +597,29 @@ public interface Result extends List, Attachable { Map, E> intoMap(Field[] keys, Class type) throws IllegalArgumentException, InvalidResultException, MappingException; + /** + * Return a {@link Map} with results grouped by the given keys and mapped by + * the given mapper. *

+ * An {@link InvalidResultException} is thrown, if the keys are non-unique + * in the result set. Use {@link #intoGroups(Field[], Class)} instead, if + * your keys are non-unique. + * + * @param keys The keys. Client code must assure that keys are unique in the + * result set. If this is null or an empty array, + * the resulting map will contain at most one entry. + * @param mapper The mapper callback. + * @return A Map containing the results. + * @throws IllegalArgumentException If any of the argument fields is not + * contained in {@link #fieldsRow()} + * @throws InvalidResultException if the keys are non-unique in the result + * set. + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + * @see DefaultRecordMapper + */ + Map, E> intoMap(Field[] keys, RecordMapper mapper) throws IllegalArgumentException, + InvalidResultException, MappingException; + /** * Return a {@link Map} with one of the result's columns as key and a list * of corresponding records as value. @@ -636,6 +684,22 @@ public interface Result extends List, Attachable { Map> intoGroups(Field key, Class type) throws IllegalArgumentException, MappingException; + /** + * Return a {@link Map} with results grouped by the given key and mapped by + * the given mapper. + * + * @param The key's generic field type + * @param The generic entity type. + * @param key The key field. + * @param mapper The mapper callback. + * @throws IllegalArgumentException If the argument field is not contained + * in {@link #fieldsRow()} + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + */ + Map> intoGroups(Field key, RecordMapper mapper) throws IllegalArgumentException, + MappingException; + /** * Return a {@link Map} with results grouped by the given keys and mapped * into the given entity type. @@ -645,6 +709,7 @@ public interface Result extends List, Attachable { * * @param keys The keys. If this is null or an empty array, the * resulting map will contain at most one entry. + * @param type The entity type. * @return A Map containing grouped results * @throws IllegalArgumentException If the any of the argument fields is not * contained in {@link #fieldsRow()} @@ -655,6 +720,26 @@ public interface Result extends List, Attachable { Map> intoGroups(Field[] keys, Class type) throws IllegalArgumentException, MappingException; + /** + * Return a {@link Map} with results grouped by the given keys and mapped + * into the given entity type. + *

+ * Unlike {@link #intoMap(Field[], Class)}, this method allows for + * non-unique keys in the result set. + * + * @param keys The keys. If this is null or an empty array, the + * resulting map will contain at most one entry. + * @param mapper The mapper callback. + * @return A Map containing grouped results + * @throws IllegalArgumentException If the any of the argument fields is not + * contained in {@link #fieldsRow()} + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + * @see DefaultRecordMapper + */ + Map> intoGroups(Field[] keys, RecordMapper mapper) + throws IllegalArgumentException, MappingException; + /** * Convert this result into an array of arrays. *

diff --git a/jOOQ/src/main/java/org/jooq/ResultQuery.java b/jOOQ/src/main/java/org/jooq/ResultQuery.java index c403412ef4..67265dd24e 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -546,6 +546,7 @@ public interface ResultQuery extends Query { * @param keys The keys. Client code must assure that keys are unique in the * result set. If this is null or an empty array, * the resulting map will contain at most one entry. + * @param type The entity type. * @return A Map containing the results. * @throws DataAccessException if something went wrong executing the query * @throws InvalidResultException if the keys are non-unique in the result @@ -557,6 +558,29 @@ public interface ResultQuery extends Query { */ Map, E> fetchMap(Field[] keys, Class type) throws MappingException; + /** + * Execute the query and return a {@link Map} with results grouped by the + * given keys and mapped by the given mapper. + *

+ * An {@link InvalidResultException} is thrown, if the keys are non-unique + * in the result set. Use {@link #fetchGroups(Field[], Class)} instead, if + * your keys are non-unique. + * + * @param keys The keys. Client code must assure that keys are unique in the + * result set. If this is null or an empty array, + * the resulting map will contain at most one entry. + * @param mapper The mapper callback. + * @return A Map containing the results. + * @throws DataAccessException if something went wrong executing the query + * @throws InvalidResultException if the keys are non-unique in the result + * set. + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + * @see Result#intoMap(Field[], Class) + * @see DefaultRecordMapper + */ + Map, E> fetchMap(Field[] keys, RecordMapper mapper) throws MappingException; + /** * Execute the query and return a {@link Map} with results grouped by the * given key and mapped into the given entity type. @@ -567,6 +591,7 @@ public interface ResultQuery extends Query { * * @param key The key. Client code must assure that key is unique in the * result set. + * @param type The entity type. * @return A Map containing the result. * @throws DataAccessException if something went wrong executing the query * @throws InvalidResultException if the key is non-unique in the result @@ -575,6 +600,25 @@ public interface ResultQuery extends Query { */ Map fetchMap(Field key, Class type) throws DataAccessException; + /** + * Execute the query and return a {@link Map} with results grouped by the + * given key and mapped by the given mapper. + *

+ * An exception is thrown, if the key turn out to be non-unique in the + * result set. Use {@link #fetchGroups(Field, Class)} instead, if your key + * is non-unique. + * + * @param key The key. Client code must assure that key is unique in the + * result set. + * @param mapper The mapper callback. + * @return A Map containing the result. + * @throws DataAccessException if something went wrong executing the query + * @throws InvalidResultException if the key is non-unique in the result + * set. + * @see Result#intoMap(Field, Class) + */ + Map fetchMap(Field key, RecordMapper mapper) throws DataAccessException; + /** * Execute the query and return a {@link Map} with one of the result's * columns as key and a list of corresponding records as value. @@ -634,6 +678,7 @@ public interface ResultQuery extends Query { * * @param keys The keys. If this is null or an empty array, the * resulting map will contain at most one entry. + * @param type The entity type. * @return A Map containing grouped results * @throws DataAccessException if something went wrong executing the query * @throws MappingException wrapping any reflection or data type conversion @@ -643,6 +688,25 @@ public interface ResultQuery extends Query { */ Map> fetchGroups(Field[] keys, Class type) throws MappingException; + /** + * Execute the query and return a {@link Map} with results grouped by the + * given keys and mapped by the given mapper. + *

+ * Unlike {@link #fetchMap(Field[], Class)}, this method allows for + * non-unique keys in the result set. + * + * @param keys The keys. If this is null or an empty array, the + * resulting map will contain at most one entry. + * @param mapper The mapper callback. + * @return A Map containing grouped results + * @throws DataAccessException if something went wrong executing the query + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + * @see Result#intoGroups(Field[], Class) + * @see DefaultRecordMapper + */ + Map> fetchGroups(Field[] keys, RecordMapper mapper) throws MappingException; + /** * Return a {@link Map} with results grouped by the given key and mapped * into the given entity type. @@ -660,6 +724,23 @@ public interface ResultQuery extends Query { Map> fetchGroups(Field key, Class type) throws DataAccessException, MappingException; + /** + * Return a {@link Map} with results grouped by the given key and mapped by + * the given mapper. + * + * @param The key's generic field type + * @param The generic entity type. + * @param key The key field. + * @param mapper The mapper callback. + * @throws DataAccessException if something went wrong executing the query + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + * @see Result#intoGroups(Field, Class) + * @see DefaultRecordMapper + */ + Map> fetchGroups(Field key, RecordMapper mapper) throws DataAccessException, + MappingException; + /** * Execute the query and return the generated result as an Object matrix *

diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index 41be55c2e2..476277f6cf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -502,11 +502,21 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().intoMap(keys, type); } + @Override + public final Map, E> fetchMap(Field[] keys, RecordMapper mapper) { + return fetch().intoMap(keys, mapper); + } + @Override public final Map fetchMap(Field key, Class type) { return fetch().intoMap(key, type); } + @Override + public final Map fetchMap(Field key, RecordMapper mapper) { + return fetch().intoMap(key, mapper); + } + @Override public final List> fetchMaps() { return fetch().intoMaps(); @@ -538,6 +548,21 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().intoGroups(keys, type); } + @Override + public final Map> fetchGroups(Field[] keys, RecordMapper mapper) { + return fetch().intoGroups(keys, mapper); + } + + @Override + public final Map> fetchGroups(Field key, Class type) { + return fetch().intoGroups(key, type); + } + + @Override + public final Map> fetchGroups(Field key, RecordMapper mapper) { + return fetch().intoGroups(key, mapper); + } + @Override public final Object[][] fetchArrays() { return fetch().intoArray(); @@ -636,11 +661,6 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().map(mapper); } - @Override - public final Map> fetchGroups(Field key, Class type) { - return fetch().intoGroups(key, type); - } - @Override @Deprecated public final org.jooq.FutureResult fetchLater() { diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java index 635eddc863..ceefd967ca 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java @@ -830,8 +830,11 @@ class ResultImpl implements Result, AttachableInternal { @Override public final Map, E> intoMap(Field[] keys, Class type) { - RecordMapper mapper = Utils.configuration(this).recordMapperProvider().provide(fields, type); + return intoMap(keys, Utils.configuration(this).recordMapperProvider().provide(fields, type)); + } + @Override + public final Map, E> intoMap(Field[] keys, RecordMapper mapper) { if (keys == null) { keys = new Field[0]; } @@ -852,10 +855,14 @@ class ResultImpl implements Result, AttachableInternal { return map; } - @SuppressWarnings("unchecked") @Override public final Map intoMap(Field key, Class type) { - RecordMapper mapper = Utils.configuration(this).recordMapperProvider().provide(fields, type); + return intoMap(key, Utils.configuration(this).recordMapperProvider().provide(fields, type)); + } + + @SuppressWarnings("unchecked") + @Override + public final Map intoMap(Field key, RecordMapper mapper) { int index = indexOrFail(fieldsRow(), key); Map map = new LinkedHashMap(); @@ -941,10 +948,14 @@ class ResultImpl implements Result, AttachableInternal { return map; } - @SuppressWarnings("unchecked") @Override public final Map> intoGroups(Field key, Class type) { - RecordMapper mapper = Utils.configuration(this).recordMapperProvider().provide(fields, type); + return intoGroups(key, Utils.configuration(this).recordMapperProvider().provide(fields, type)); + } + + @SuppressWarnings("unchecked") + @Override + public final Map> intoGroups(Field key, RecordMapper mapper) { int index = indexOrFail(fieldsRow(), key); Map> map = new LinkedHashMap>(); @@ -965,8 +976,11 @@ class ResultImpl implements Result, AttachableInternal { @Override public final Map> intoGroups(Field[] keys, Class type) { - RecordMapper mapper = Utils.configuration(this).recordMapperProvider().provide(fields, type); + return intoGroups(keys, Utils.configuration(this).recordMapperProvider().provide(fields, type)); + } + @Override + public final Map> intoGroups(Field[] keys, RecordMapper mapper) { if (keys == null) { keys = new Field[0]; } diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java index 6a79da57f5..23b7394860 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java @@ -2397,11 +2397,21 @@ class SelectImpl Map, E> fetchMap(Field[] keys, RecordMapper mapper) { + return getDelegate().fetchMap(keys, mapper); + } + @Override public final Map fetchMap(Field key, Class type) { return getDelegate().fetchMap(key, type); } + @Override + public final Map fetchMap(Field key, RecordMapper mapper) { + return getDelegate().fetchMap(key, mapper); + } + @Override public final List> fetchMaps() { return getDelegate().fetchMaps(); @@ -2432,6 +2442,21 @@ class SelectImpl Map> fetchGroups(Field[] keys, RecordMapper mapper) { + return getDelegate().fetchGroups(keys, mapper); + } + + @Override + public final Map> fetchGroups(Field key, Class type) { + return getDelegate().fetchGroups(key, type); + } + + @Override + public final Map> fetchGroups(Field key, RecordMapper mapper) { + return getDelegate().fetchGroups(key, mapper); + } + @Override public final Object[][] fetchArrays() { return getDelegate().fetchArrays(); @@ -2517,11 +2542,6 @@ class SelectImpl Map> fetchGroups(Field key, Class type) { - return getDelegate().fetchGroups(key, type); - } - @Override @Deprecated public final org.jooq.FutureResult fetchLater() {