Merge pull request #606 from Jugen/use-in-clause-for-single-field-disjoint-by-id-prefetches

CAY-2843 Use an IN clause for single field disjoint by ID prefetches
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 5b07858..1658d19 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -63,6 +63,7 @@
 CAY-2831 Upgrade Gradle to 8.5
 CAY-2834 v11 upgrade handler should update XMLPoolingDataSourceFactory package
 CAY-2837 Deprecate TraversalHelper and move no-op implementations to TraversalHandler
+CAY-2843 Use an IN clause for single field disjoint by ID prefetches
 
 Bug Fixes:
 
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java b/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
index a9f6a0f..934c8ed 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
@@ -38,6 +38,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Processes a number of DataRow sets corresponding to a given prefetch tree, resolving
@@ -205,8 +206,19 @@
 
         private void createDisjointByIdPrefetchQualifier(String pathPrefix, PrefetchSelectQuery<?> currentQuery,
                                                          List<DbJoin> joins, Set<List<Object>> values) {
-            Expression allJoinsQualifier;
-            if(currentQuery != null) {
+            if (currentQuery == null) return;
+
+             // Use an IN clause for the list of prefetch IDs, when the
+             // join ON clause only has one predicate with many values.
+             // Results in SQL:  ... targetField IN ( ?, ?, ?, .... )
+            if (joins.size() == 1 && values.size() > 1) {
+                currentQuery.and( ExpressionFactory.inDbExp(
+                    pathPrefix + joins.get(0).getTargetName(),
+                    values.stream().flatMap(List::stream).collect(Collectors.toSet())
+                ));
+            } else { // Handle a single value or compound prefetch ID predicates
+            	// SQL: ... (field1=? and field2=? ...) OR (field1=? and field2=? ...) etc
+            	Expression allJoinsQualifier;
                 Expression[] qualifiers = new Expression[values.size()];
                 int i = 0;
                 for(List<Object> joinValues : values) {