You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2025/10/18 02:48:09 UTC
(grails-core) 03/04: fixed WhereQueryWithAssociationSortSpec
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 <wb...@mac.com>
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) {