diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java index 3b0d8bfa8b..9ed9b2fcf8 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/FetchTests.java @@ -1687,6 +1687,7 @@ extends BaseTest POJO // Group by BOOK.ID Map map1 = create().selectFrom(TBook()) @@ -1716,6 +1717,29 @@ extends BaseTest POJO + // Grouping by BOOK.ID, BOOK.LANGUAGE_ID, BOOK.TITLE + Map, Object> map4 = create().selectFrom(TBook()).orderBy(TBook_ID()) + .fetchMap(new Field[] { TBook_ID(), TBook_LANGUAGE_ID(), TBook_TITLE() }, TBookPojo()); + assertEquals(4, map4.keySet().size()); + + for (List keys : map4.keySet()) { + Object pojo = map4.get(keys); + assertEquals(keys.get(0), on(pojo).call("getId").get()); + assertEquals(keys.get(1), on(pojo).call("getLanguageId").get()); + assertEquals(keys.get(2), on(pojo).call("getTitle").get()); + } + + try { + // Grouping by BOOK.AUTHOR_ID + create().selectFrom(TBook()).orderBy(TBook_ID()) + .fetchMap(new Field[] { TBook_AUTHOR_ID(), }, TBookPojo()); + fail("Fetching map with the non-unique key - InvalidResultException not thrown."); + } + catch (Throwable t) { + assertEquals(InvalidResultException.class, t.getClass()); + } } @Test @@ -1725,6 +1749,7 @@ extends BaseTest POJO // Group by BOOK.ID Map> map1 = create().selectFrom(TBook()) @@ -1775,6 +1800,44 @@ extends BaseTest POJO + // Grouping by BOOK.AUTHOR_ID, BOOK.LANGUAGE_ID + Map, List> map3 = create().selectFrom(TBook()).orderBy(TBook_ID()) + .fetchGroups(new Field[] { TBook_AUTHOR_ID(), TBook_LANGUAGE_ID() }, TBookPojo()); + + Iterator, List>> iterator = map3.entrySet().iterator(); + Entry, List> entry1_en = iterator.next(); + assertEquals(2, entry1_en.getValue().size()); + assertEquals(entry1_en.getKey().get(0), on(entry1_en.getValue().get(0)).call("getAuthorId").get()); + assertEquals(entry1_en.getKey().get(0), on(entry1_en.getValue().get(1)).call("getAuthorId").get()); + assertEquals(entry1_en.getKey().get(1), on(entry1_en.getValue().get(0)).call("getLanguageId").get()); + assertEquals(entry1_en.getKey().get(1), on(entry1_en.getValue().get(1)).call("getLanguageId").get()); + + Entry, List> entry2_pt = iterator.next(); + assertEquals(1, entry2_pt.getValue().size()); + assertEquals(entry2_pt.getKey().get(0), on(entry2_pt.getValue().get(0)).call("getAuthorId").get()); + assertEquals(entry2_pt.getKey().get(1), on(entry2_pt.getValue().get(0)).call("getLanguageId").get()); + + Entry, List> entry2_de = iterator.next(); + assertEquals(1, entry2_de.getValue().size()); + assertEquals(entry2_de.getKey().get(0), on(entry2_de.getValue().get(0)).call("getAuthorId").get()); + assertEquals(entry2_de.getKey().get(1), on(entry2_de.getValue().get(0)).call("getLanguageId").get()); + + assertFalse(iterator.hasNext()); + + // Grouping by BOOK.AUTHOR_ID, BOOK.LANGUAGE_ID, BOOK.TITLE + Map, List> map4 = create().selectFrom(TBook()).orderBy(TBook_ID()) + .fetchGroups(new Field[] { TBook_ID(), TBook_LANGUAGE_ID(), TBook_TITLE() }, TBookPojo()); + assertEquals(4, map4.size()); + + for (List keyList : map4.keySet()) { + List result = map4.get(keyList); + assertEquals(1, result.size()); + assertEquals(keyList.get(0), on(result.get(0)).call("getId").get()); + assertEquals(keyList.get(1), on(result.get(0)).call("getLanguageId").get()); + assertEquals(keyList.get(2), on(result.get(0)).call("getTitle").get()); + } } @Test diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index 5fa0b849c0..000fce7d2f 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -1865,9 +1865,30 @@ public interface Result extends FieldProvider, List, Attach * @return A Map containing the result. * @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 */ Map intoMap(Field key, Class type) throws MappingException; + /** + * Return a {@link Map} with results grouped by the given keys and mapped + * into the given entity type. + *

+ * 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. + * @return A Map containing the results. + * @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 + */ + Map, E> intoMap(Field[] keys, Class type) throws MappingException; + /** * Return a {@link Map} with one of the result's columns as key and a list * of corresponding records as value. @@ -1897,8 +1918,7 @@ public interface Result extends FieldProvider, List, Attach Map> intoGroups(Field key, Field value); /** - * Execute the query and return a {@link Map} with the result grouped by the - * given keys. + * Return a {@link Map} with the result grouped by the given keys. *

* Unlike {@link #intoMap(Field[])}, this method allows for non-unique keys * in the result set. @@ -1913,7 +1933,6 @@ public interface Result extends FieldProvider, List, Attach * Return a {@link Map} with results grouped by the given key and mapped * into the given entity type. *

- * * @param The key's generic field type * @param The generic entity type. * @param key The key field. @@ -1923,6 +1942,22 @@ public interface Result extends FieldProvider, List, Attach */ Map> intoGroups(Field key, Class type) throws 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. + * @return A Map containing grouped results + * @throws MappingException wrapping any reflection or data type conversion + * exception that might have occurred while mapping records + */ + Map, List> intoGroups(Field[] keys, Class type) throws 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 26124b44e3..caec62359d 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -519,6 +519,27 @@ public interface ResultQuery extends Query { */ Map fetchMap(Field[] keys) throws DataAccessException; + /** + * Execute the query and return a {@link Map} with results grouped by the + * given keys and mapped into the given entity type. + *

+ * 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. + * @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) + */ + Map, E> fetchMap(Field[] keys, Class type) throws MappingException; + /** * Execute the query and return a {@link Map} with results grouped by the * given key and mapped into the given entity type. @@ -587,6 +608,23 @@ public interface ResultQuery extends Query { */ Map> fetchGroups(Field[] keys) throws DataAccessException; + /** + * Execute the query and return a {@link Map} with results grouped by the + * given keys and mapped into the given entity type. + *

+ * 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. + * @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) + */ + Map, List> fetchGroups(Field[] keys, Class type) throws MappingException; + /** * Return a {@link Map} with results grouped by the given key and mapped * into the given entity type. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java index 7a8b51b9a5..377d056817 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java @@ -249,6 +249,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetchMap(keys); } + @Override + public final Map, E> fetchMap(Field[] keys, Class type) { + return getDelegate().fetchMap(keys, type); + } + @Override public final Map fetchMap(Field key, Class type) { return getDelegate().fetchMap(key, type); @@ -279,6 +284,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetchGroups(keys); } + @Override + public final Map, List> fetchGroups(Field[] keys, Class type) { + return getDelegate().fetchGroups(keys, type); + } + @Override public final Object[][] fetchArrays() { return getDelegate().fetchArrays(); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index cdd3231770..2924b5dfba 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -445,6 +445,11 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().intoMap(keys); } + @Override + public final Map, E> fetchMap(Field[] keys, Class type) { + return fetch().intoMap(keys, type); + } + @Override public final Map fetchMap(Field key, Class type) { return fetch().intoMap(key, type); @@ -475,6 +480,11 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().intoGroups(keys); } + @Override + public final Map, List> fetchGroups(Field[] keys, Class type) { + return fetch().intoGroups(keys, type); + } + @Override public final Object[][] fetchArrays() { return fetch().intoArray(); diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java index 1417d66cd1..ab5aad2fe9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java @@ -1416,6 +1416,28 @@ class ResultImpl implements Result, AttachableInternal { return map; } + @Override + public final Map, E> intoMap(Field[] keys, Class type) { + if (keys == null) { + keys = new Field[0]; + } + + Map, E> map = new LinkedHashMap, E>(); + + for (R record : this) { + List keyValueList = new ArrayList(); + for (Field key : keys) { + keyValueList.add(record.getValue(key)); + } + + if (map.put(keyValueList, record.into(type)) != null) { + throw new InvalidResultException("Key list " + keyValueList + " is not unique in Result for " + this); + } + } + + return map; + } + @Override public final Map intoMap(Field key, Class type) { Map map = new LinkedHashMap(); @@ -1515,6 +1537,32 @@ class ResultImpl implements Result, AttachableInternal { return map; } + @Override + public final Map, List> intoGroups(Field[] keys, Class type) { + if (keys == null) { + keys = new Field[0]; + } + + Map, List> map = new LinkedHashMap, List>(); + + for (R record : this) { + List keyValueList = new ArrayList(); + for (Field key : keys) { + keyValueList.add(record.getValue(key)); + } + + List list = map.get(keyValueList); + if (list == null) { + list = new ArrayList(); + map.put(keyValueList, list); + } + + list.add(record.into(type)); + } + + return map; + } + @Override public final Object[][] intoArray() { int size = size();