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

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

commit 9d31dff67533c48c1b77f39f01726226f01a88eb
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Thu Mar 19 22:44:14 2026 -0500

    hibernate 7:
    
     JpaFromProvider fixed
---
 .../orm/hibernate/query/JpaFromProvider.java       | 38 ++++++++++++----------
 .../hibernatequery/JpaFromProviderSpec.groovy      | 18 +++++-----
 2 files changed, 30 insertions(+), 26 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
index 8c1f9d65fe..7cd7b15858 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
@@ -18,16 +18,12 @@
  */
 package org.grails.orm.hibernate.query;
 
-import java.util.AbstractMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import jakarta.persistence.FetchType;
 import jakarta.persistence.criteria.AbstractQuery;
@@ -55,32 +51,29 @@ public class JpaFromProvider implements Cloneable {
         this.fromMap = new HashMap<>(fromMap);
     }
 
-    public JpaFromProvider(DetachedCriteria<?> detachedCriteria, 
AbstractQuery<?> cq, From<?, ?> root) {
-        this(detachedCriteria, List.of(), cq, root);
-    }
-
     public JpaFromProvider(
             DetachedCriteria<?> detachedCriteria,
             List<Query.Projection> projections,
-            AbstractQuery<?> cq,
             From<?, ?> root) {
-        fromMap = getFromsByName(detachedCriteria, projections, cq, root);
+        fromMap = getFromsByName(detachedCriteria, projections, root);
     }
 
     public JpaFromProvider(
             JpaFromProvider parent,
             DetachedCriteria<?> detachedCriteria,
             List<Query.Projection> projections,
-            AbstractQuery<?> cq,
             From<?, ?> root) {
         fromMap = new HashMap<>(parent.fromMap);
-        fromMap.putAll(getFromsByName(detachedCriteria, projections, cq, 
root));
+        fromMap.putAll(getFromsByName(detachedCriteria, projections, root));
+    }
+
+    public Map<String, From<?, ?>> getFromsByName() {
+        return fromMap;
     }
 
-    private Map<String, From<?, ?>> getFromsByName(
+    protected Map<String, From<?, ?>> getFromsByName(
             DetachedCriteria<?> detachedCriteria,
             List<Query.Projection> projections,
-            AbstractQuery<?> cq,
             From<?, ?> root) {
         var detachedAssociationCriteriaList = 
detachedCriteria.getCriteria().stream()
                 .map(new DetachedAssociationFunction())
@@ -93,7 +86,7 @@ public class JpaFromProvider implements Cloneable {
                 .filter(Objects::nonNull)
                 .collect(Collectors.toSet());
 
-        var projectedPaths = projections.stream()
+        var directProjectedPaths = projections.stream()
                 .filter(Query.PropertyProjection.class::isInstance)
                 .map(p -> ((Query.PropertyProjection) p).getPropertyName())
                 .filter(name -> name.contains("."))
@@ -107,7 +100,7 @@ public class JpaFromProvider implements Cloneable {
 
         java.util.Set<String> allPaths = new java.util.HashSet<>();
         allPaths.addAll(aliasMap.keySet());
-        allPaths.addAll(projectedPaths.stream()
+        allPaths.addAll(directProjectedPaths.stream()
                 .filter(p -> !definedAliases.contains(p))
                 .toList());
         allPaths.addAll(eagerPaths);
@@ -126,6 +119,11 @@ public class JpaFromProvider implements Cloneable {
             }
         }
 
+        // Re-calculate projected paths to include expanded segments for LEFT 
join logic
+        var finalProjectedPaths = expandedPaths.stream()
+                .filter(p -> directProjectedPaths.stream().anyMatch(dp -> 
dp.equals(p) || dp.startsWith(p + ".")))
+                .collect(Collectors.toSet());
+
         Map<String, From<?, ?>> fromsByPath = new HashMap<>();
         fromsByPath.put("root", root);
 
@@ -145,7 +143,7 @@ public class JpaFromProvider implements Cloneable {
             JoinType joinType = JoinType.INNER;
             if (detachedCriteria.getJoinTypes().containsKey(path)) {
                 joinType = detachedCriteria.getJoinTypes().get(path);
-            } else if (projectedPaths.contains(path) || 
eagerPaths.contains(path)) {
+            } else if (finalProjectedPaths.contains(path) || 
eagerPaths.contains(path)) {
                 joinType = JoinType.LEFT;
             }
 
@@ -208,7 +206,11 @@ public class JpaFromProvider implements Cloneable {
 
         String[] parsed = propertyName.split("\\.");
         if (parsed.length == SINGLE_PROPERTY) {
-            return fromMap.get("root").get(propertyName);
+            From<?, ?> root = fromMap.get("root");
+            if (propertyName.equals(root.getJavaType().getSimpleName()) || 
propertyName.equals(root.getJavaType().getName())) {
+                return root;
+            }
+            return root.get(propertyName);
         }
 
         // Try to find the longest matching prefix in fromMap
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaFromProviderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaFromProviderSpec.groovy
index 63e454b1b5..ce424454af 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaFromProviderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaFromProviderSpec.groovy
@@ -1,11 +1,13 @@
 package grails.gorm.specs.hibernatequery
 
+import org.hibernate.query.criteria.JpaCriteriaQuery
+
 import grails.gorm.DetachedCriteria
 import grails.gorm.specs.HibernateGormDatastoreSpec
 import jakarta.persistence.criteria.From
+import jakarta.persistence.criteria.Join
 import jakarta.persistence.criteria.Path
 import org.grails.orm.hibernate.query.JpaFromProvider
-import grails.orm.HibernateCriteriaBuilder
 import grails.gorm.annotation.Entity
 import org.grails.datastore.gorm.GormEntity
 
@@ -17,10 +19,10 @@ class JpaFromProviderSpec extends 
HibernateGormDatastoreSpec {
 
     private JpaFromProvider bare(Class clazz, From root) {
         def dc = new DetachedCriteria(clazz)
-        def cq = Mock(org.hibernate.query.criteria.JpaCriteriaQuery) {
+        def cq = Mock(JpaCriteriaQuery) {
             from(clazz) >> root
         }
-        return new JpaFromProvider(dc, cq, root)
+        return new JpaFromProvider(dc, [], root)
     }
 
     def "getFromsByName returns root for 'root' key"() {
@@ -114,7 +116,7 @@ class JpaFromProviderSpec extends 
HibernateGormDatastoreSpec {
         def cq = Mock(org.hibernate.query.criteria.JpaCriteriaQuery) {
             from(_) >> root
         }
-        JpaFromProvider provider = new JpaFromProvider(dc, cq, root)
+        JpaFromProvider provider = new JpaFromProvider(dc, [], root)
 
         when:
         Path result = provider.getFullyQualifiedPath("myAlias.id")
@@ -130,11 +132,11 @@ class JpaFromProviderSpec extends 
HibernateGormDatastoreSpec {
         From root = Mock(From) {
             getJavaType() >> String
         }
-        From teamJoin = Mock(From) {
+        Join teamJoin = Mock(Join) {
             getJavaType() >> String
             alias(_) >> it
         }
-        From clubJoin = Mock(From) {
+        Join clubJoin = Mock(Join) {
             getJavaType() >> String
             alias(_) >> it
         }
@@ -145,7 +147,7 @@ class JpaFromProviderSpec extends 
HibernateGormDatastoreSpec {
         ]
 
         when:
-        JpaFromProvider provider = new JpaFromProvider(dc, projections, cq, 
root)
+        JpaFromProvider provider = new JpaFromProvider(dc, projections, root)
 
         then: "joins are created hierarchically"
         1 * root.join("team", jakarta.persistence.criteria.JoinType.LEFT) >> 
teamJoin
@@ -168,7 +170,7 @@ class JpaFromProviderSpec extends 
HibernateGormDatastoreSpec {
         From subRoot = Mock(From) { getJavaType() >> Integer }
 
         when:
-        JpaFromProvider subProvider = new JpaFromProvider(parent, subDc, [], 
subCq, subRoot)
+        JpaFromProvider subProvider = new JpaFromProvider(parent, subDc, [], 
subRoot)
 
         then: "subquery provider has its own root"
         subProvider.getFullyQualifiedPath("root") == subRoot

Reply via email to