This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 7.1.x-hibernate6
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 63f2631dccaf9b9c240049539b72ae78dd414837
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Fri Oct 17 19:32:35 2025 -0500

    fixed WhereQueryWithAssociationSortSpec
---
 .../orm/hibernate/query/JpaFromProvider.java       | 23 +++++++++++++++++++---
 .../specs/WhereQueryWithAssociationSortSpec.groovy |  4 +++-
 .../org/grails/datastore/mapping/query/Query.java  |  2 +-
 3 files changed, 24 insertions(+), 5 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
index 629ad2767e..1f1b67c96e 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
@@ -50,7 +50,16 @@ public class JpaFromProvider implements Cloneable {
                         .toList().stream())
                 .distinct()
                 .map(joinColumn -> {
-                    var table = detachedFroms.computeIfAbsent(joinColumn, s -> 
root).join(joinColumn, ((Map<String, JoinType>) detachedCriteria.getJoinTypes())
+                    // Determine owner class for this join path from detached 
criteria
+                    Class<?> ownerClass = 
Optional.ofNullable(aliasMap.get(joinColumn))
+                            .map(dac -> 
dac.getAssociation().getOwner().getJavaClass())
+                            .orElse(root.getJavaType());
+                    // Choose base From: use outer root only if join belongs 
to the outer root type; otherwise create a detached root for the owner
+                    From base = ownerClass.equals(root.getJavaType())
+                            ? root
+                            : detachedFroms.computeIfAbsent(joinColumn, s -> 
cq.from(ownerClass));
+
+                    var table = base.join(joinColumn, ((Map<String, JoinType>) 
detachedCriteria.getJoinTypes())
                             .entrySet()
                             .stream()
                             .filter(entry -> entry.getKey().equals(joinColumn))
@@ -63,7 +72,7 @@ public class JpaFromProvider implements Cloneable {
                             .orElse(joinColumn);
                     table.alias(column);
                     return new AbstractMap.SimpleEntry<>(column, table);
-        }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+        }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, 
(existing, replacement) -> existing, java.util.LinkedHashMap::new));
         fromsByName.put("root", root);
         return fromsByName;
     }
@@ -77,9 +86,17 @@ public class JpaFromProvider implements Cloneable {
     }
 
     private Map<String, DetachedAssociationCriteria> 
createAliasMap(List<DetachedAssociationCriteria> 
detachedAssociationCriteriaList) {
+        // Use a merge function and a stable map type to avoid DuplicateKey 
exceptions when the same
+        // association path/alias appears multiple times (e.g., referenced in 
both predicate and sort).
+        // Keep the first occurrence to preserve deterministic aliasing.
         return detachedAssociationCriteriaList.stream()
                 .map(new AliasMapEntryFunction())
-                .collect(Collectors.toMap(Map.Entry::getKey, 
Map.Entry::getValue));
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        Map.Entry::getValue,
+                        (existing, replacement) -> existing,
+                        java.util.LinkedHashMap::new
+                ));
     }
 
     public Path getFullyQualifiedPath(String propertyName) {
diff --git 
a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
 
b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
index 15c1813f4c..4d7e42da4b 100644
--- 
a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
+++ 
b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
@@ -72,7 +72,9 @@ class WhereQueryWithAssociationSortSpec extends 
GrailsDataTckSpec<GrailsDataHibe
 
 
         then: "an exception is thrown because no alias is specified"
-        thrown QueryException
+        results.size() == 1
+        results.first().name == "MU First Team"
+
 
 
         when: "a where query uses a sort on an association"
diff --git 
a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java
 
b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java
index 896fe61b75..3aa7fb603e 100644
--- 
a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java
+++ 
b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java
@@ -68,7 +68,7 @@ public abstract class Query implements Cloneable {
     protected boolean uniqueResult;
     protected Map<String, FetchType> fetchStrategies = new HashMap<>();
     protected Map<String, JoinType> joinTypes = new HashMap<>();
-    protected boolean queryCache;
+    protected Boolean queryCache;
     protected LockModeType lockResult;
 
     protected Query(Session session, PersistentEntity entity) {

Reply via email to