diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericCollection.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericCollection.java index f9a5f96ac..6e362a89a 100644 --- a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericCollection.java +++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericCollection.java @@ -13,6 +13,8 @@ */ package com.querydsl.core.group; +import static com.querydsl.core.util.TupleUtils.toTuple; + import com.querydsl.core.FetchableQuery; import com.querydsl.core.Tuple; import com.querydsl.core.types.Expression; @@ -53,29 +55,30 @@ public RES transform(FetchableQuery query) { if (hasGroups) { expr = withoutGroupExpressions(expr); } - final var iter = query.select(expr).iterate(); + try (final var iter = query.select(expr).iterate(); ) { - var list = resultFactory.get(); - GroupImpl group = null; - K groupId = null; - while (iter.hasNext()) { - @SuppressWarnings("unchecked") // This type is mandated by the key type - var row = (K[]) iter.next().toArray(); - if (group == null) { - group = new GroupImpl(groupExpressions, maps); - groupId = row[0]; - } else if (!Objects.equals(groupId, row[0])) { + var list = resultFactory.get(); + GroupImpl group = null; + K groupId = null; + while (iter.hasNext()) { + var tuple = toTuple(iter.next(), expressions); + @SuppressWarnings("unchecked") // This type is mandated by the key type + var row = (K[]) tuple.toArray(); + if (group == null) { + group = new GroupImpl(groupExpressions, maps); + groupId = row[0]; + } else if (!Objects.equals(groupId, row[0])) { + list.add(transform(group)); + group = new GroupImpl(groupExpressions, maps); + groupId = row[0]; + } + group.add(row); + } + if (group != null) { list.add(transform(group)); - group = new GroupImpl(groupExpressions, maps); - groupId = row[0]; } - group.add(row); - } - if (group != null) { - list.add(transform(group)); + return list; } - iter.close(); - return list; } @SuppressWarnings("unchecked") diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericMap.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericMap.java index 0418a0b32..eeed50baa 100644 --- a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericMap.java +++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByGenericMap.java @@ -13,6 +13,8 @@ */ package com.querydsl.core.group; +import static com.querydsl.core.util.TupleUtils.toTuple; + import com.querydsl.core.FetchableQuery; import com.querydsl.core.Tuple; import com.querydsl.core.types.Expression; @@ -53,11 +55,11 @@ public RES transform(FetchableQuery query) { if (hasGroups) { expr = withoutGroupExpressions(expr); } - var iter = query.select(expr).iterate(); - try { + try (var iter = query.select(expr).iterate()) { while (iter.hasNext()) { + var tuple = toTuple(iter.next(), expressions); @SuppressWarnings("unchecked") // This type is mandated by the key type - var row = (K[]) iter.next().toArray(); + var row = (K[]) tuple.toArray(); var groupId = row[0]; var group = (GroupImpl) groups.get(groupId); if (group == null) { @@ -66,8 +68,6 @@ public RES transform(FetchableQuery query) { } group.add(row); } - } finally { - iter.close(); } // transform groups diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByIterate.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByIterate.java index 02e78b54d..d7f0d8899 100644 --- a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByIterate.java +++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByIterate.java @@ -13,6 +13,8 @@ */ package com.querydsl.core.group; +import static com.querydsl.core.util.TupleUtils.toTuple; + import com.mysema.commons.lang.CloseableIterator; import com.querydsl.core.FetchableQuery; import com.querydsl.core.Tuple; @@ -73,8 +75,9 @@ public V next() { } while (iter.hasNext()) { + var tuple = toTuple(iter.next(), expressions); @SuppressWarnings("unchecked") // This type is mandated by the key type - var row = (K[]) iter.next().toArray(); + var row = (K[]) tuple.toArray(); if (group == null) { group = new GroupImpl(groupExpressions, maps); groupId = row[0]; diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByMap.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByMap.java index eb50a0e41..b9527dc13 100644 --- a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByMap.java +++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/group/GroupByMap.java @@ -13,6 +13,8 @@ */ package com.querydsl.core.group; +import static com.querydsl.core.util.TupleUtils.toTuple; + import com.querydsl.core.FetchableQuery; import com.querydsl.core.Tuple; import com.querydsl.core.types.Expression; @@ -50,8 +52,9 @@ public Map transform(FetchableQuery query) { } try (var iter = query.select(expr).iterate()) { while (iter.hasNext()) { + var tuple = toTuple(iter.next(), expressions); @SuppressWarnings("unchecked") // This type is mandated by the key type - var row = (K[]) iter.next().toArray(); + var row = (K[]) tuple.toArray(); var groupId = row[0]; var group = (GroupImpl) groups.get(groupId); if (group == null) { diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/util/TupleUtils.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/util/TupleUtils.java new file mode 100644 index 000000000..86ef005d2 --- /dev/null +++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/util/TupleUtils.java @@ -0,0 +1,22 @@ +package com.querydsl.core.util; + +import com.querydsl.core.Tuple; +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.Projections; + +/** TupleUtils provides tuple related utility functionality */ +public final class TupleUtils { + + public static Tuple toTuple(Object next, Expression[] expressions) { + // workaround from https://github.com/querydsl/querydsl/issues/3264 + Tuple tuple; + if (next instanceof Tuple) { + tuple = (Tuple) next; + } else if (next instanceof Object[]) { + tuple = Projections.tuple(expressions).newInstance((Object[]) next); + } else { + throw new IllegalArgumentException(String.format("Could not translate %s into tuple", next)); + } + return tuple; + } +} diff --git a/querydsl-libraries/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateBase.java b/querydsl-libraries/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateBase.java index 698c8652b..4b2871944 100644 --- a/querydsl-libraries/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateBase.java +++ b/querydsl-libraries/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateBase.java @@ -14,10 +14,14 @@ package com.querydsl.jpa; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import com.mysema.commons.lang.CloseableIterator; import com.querydsl.core.DefaultQueryMetadata; +import com.querydsl.core.Target; import com.querydsl.core.Tuple; +import com.querydsl.core.group.GroupBy; +import com.querydsl.core.testutil.ExcludeIn; import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.Expression; import com.querydsl.jpa.domain.Cat; @@ -159,4 +163,17 @@ public void createQuery3() { assertThat(row instanceof String).isTrue(); } } + + @Test + @ExcludeIn(Target.DERBY) + public void createQuery4() { + assertDoesNotThrow( + () -> + query() + .from(cat) + .leftJoin(cat.kittens) + .fetchJoin() + .distinct() + .transform(GroupBy.groupBy(cat.id).as(cat))); + } }