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

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

commit 773f97c652b647bd1fafceceb09ad8f84b4dcbe7
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sat Jun 21 17:26:52 2025 -0500

    Heavy refactoring
---
 .../hibernate/query/AbstractHibernateQuery.java    | 147 +++++++--------------
 .../orm/hibernate/query/AliasMapEntryFunction.java |  16 +++
 .../query/DetachedAssociationFunction.java         |  30 +++++
 .../orm/hibernate/query/JpaFromProvider.java       | 105 +++++++++++++++
 .../orm/hibernate/query/ProjectionPredicate.java   |  47 +++++++
 5 files changed, 245 insertions(+), 100 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java
index c165cd758c..9fedb54bed 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java
@@ -38,6 +38,7 @@ import org.grails.orm.hibernate.IHibernateTemplate;
 import org.hibernate.NonUniqueResultException;
 import org.hibernate.SessionFactory;
 import org.hibernate.query.criteria.HibernateCriteriaBuilder;
+import org.hibernate.query.criteria.JpaCriteriaQuery;
 import org.hibernate.query.criteria.JpaExpression;
 import org.hibernate.transform.ResultTransformer;
 import org.springframework.core.convert.ConversionService;
@@ -489,43 +490,19 @@ public abstract class AbstractHibernateQuery extends 
Query {
     }
 
     protected org.hibernate.query.Query createQuery() {
-        HibernateCriteriaBuilder cb = getCriteriaBuilder();
-
-
-        List<DetachedAssociationCriteria> detachedAssociationCriteria = 
getDetachedAssociationCriteria();
-
-        Map<String, DetachedAssociationCriteria> aliasMap = 
detachedAssociationCriteria.stream()
-                .collect(Collectors.toMap(
-                        DetachedAssociationCriteria::getAssociationPath,
-                    criteria ->criteria, (oldValue,newValue) -> newValue)
-                );
 
+        var projectionList = collectProjections();
+        var cq = createCriteriaQuery(projectionList);
+        From root = cq.from(entity.getJavaClass());
+        var jpaFromProvider = new JpaFromProvider(detachedCriteria);
+        Map<String, From> tablesByName = jpaFromProvider.getFromsByName(cq, 
root);
 
-        List<Projection> projections = collectProjections();
+        assignProjections(projectionList, cq, tablesByName);
 
         List<GroupPropertyProjection> groupProjections = 
collectGroupProjections();
-
-        List<String> joinColumns = Stream.concat(aliasMap.keySet().stream(), 
collectJoinColumns().stream()).distinct().toList();
-        CriteriaQuery cq = projections.stream()
-                .filter( it -> !(it instanceof DistinctProjection || it 
instanceof DistinctPropertyProjection))
-                .toList().size() > 1 ?  cb.createQuery(Object[].class) : 
cb.createQuery(Object.class);
-        projections.stream()
-                .filter( it -> it instanceof DistinctProjection || it 
instanceof DistinctPropertyProjection)
-                .findFirst()
-                .ifPresent(projection -> {
-                    cq.distinct(true);
-                });
-        From root = cq.from(entity.getJavaClass());
-        Map<String, From> fromMap = detachedAssociationCriteria.stream()
-                .collect(Collectors.toMap(
-                        DetachedAssociationCriteria::getAssociationPath,
-                        criteria -> 
cq.from(criteria.getAssociation().getOwner().getJavaClass()) , 
(oldValue,newValue) -> newValue)
-                );
-        fromMap.put("root", root);
-        Map<String, From> tablesByName = assignJoinTables(joinColumns, 
root,aliasMap, fromMap);
-        assignProjections(projections, cb, root, cq, tablesByName);
         assignGroupBy(groupProjections, root, cq, tablesByName);
-        assignOrderBy(cq, cb, tablesByName);
+        var cb = getCriteriaBuilder();
+        assignOrderBy(cq, tablesByName);
         assignCriteria(cq, cb, root,tablesByName);
 
         org.hibernate.query.Query query = getSessionFactory()
@@ -545,26 +522,38 @@ public abstract class AbstractHibernateQuery extends 
Query {
         return query;
     }
 
+
+
+    @SuppressWarnings("unchecked")
+    private Map<String, JoinType> getDetachedCriteriaJoinTypes() {
+        return detachedCriteria.getJoinTypes();
+    }
+
+    private JpaCriteriaQuery<?> createCriteriaQuery(List<Projection> 
projections) {
+        var cb = getCriteriaBuilder();
+        var cq = projections.stream()
+                .filter( it -> !(it instanceof DistinctProjection || it 
instanceof DistinctPropertyProjection))
+                .toList().size() > 1 ?  cb.createQuery(Object[].class) : 
cb.createQuery(Object.class);
+        projections.stream()
+                .filter( it -> it instanceof DistinctProjection || it 
instanceof DistinctPropertyProjection)
+                .findFirst()
+                .ifPresent(projection -> {
+                    cq.distinct(true);
+                });
+        return cq;
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<Query.Criterion> getDetachedCriteria() {
+        return detachedCriteria.getCriteria();
+    }
+
     private List<DetachedAssociationCriteria> getDetachedAssociationCriteria() 
{
-        List<DetachedAssociationCriteria> detachedAssociationCriteria = 
detachedCriteria.getCriteria()
+        return getDetachedCriteria()
                 .stream()
-                .map(o -> {
-                    if (o instanceof In c && Objects.nonNull(c.getSubquery()) 
) {
-                        return c.getSubquery().getCriteria();
-                    } else if (o instanceof Exists c && 
Objects.nonNull(c.getSubquery()) ) {
-                        return c.getSubquery().getCriteria();
-                    } else if (o instanceof NotExists c && 
Objects.nonNull(c.getSubquery()) ) {
-                        return c.getSubquery().getCriteria();
-                    } else if (o instanceof SubqueryCriterion c && 
Objects.nonNull(c.getValue()) ) {
-                        return c.getValue().getCriteria();
-                    }
-                    return List.of(o);
-                })
-                .flatMap(list -> ((List) list).stream())
-                .filter(DetachedAssociationCriteria.class::isInstance)
-                .map(DetachedAssociationCriteria.class::cast)
+                .map(new DetachedAssociationFunction())
+                .flatMap(List::stream)
                 .toList();
-        return detachedAssociationCriteria;
     }
 
     private List<String> collectJoinColumns() {
@@ -587,22 +576,22 @@ public abstract class AbstractHibernateQuery extends 
Query {
     }
 
     private List<Projection> collectProjections() {
-        List<Projection> projections = projections().getProjectionList()
+        return projections().getProjectionList()
                 .stream()
-                .filter(combinePredicates(projectionPredicates))
+                .filter(new ProjectionPredicate())
                 .toList();
-        return projections;
     }
 
     private void assignCriteria(CriteriaQuery cq, HibernateCriteriaBuilder cb, 
From root, Map<String, From> tablesByName) {
-        List<Criterion>  criteriaList = 
(List<Criterion>)detachedCriteria.getCriteria();
+        List<Criterion>  criteriaList = getDetachedCriteria();
         if (!criteriaList.isEmpty()) {
             jakarta.persistence.criteria.Predicate[] predicates = 
PredicateGenerator.getPredicates(cb, cq, root, criteriaList, tablesByName);
             cq.where(cb.and(predicates));
         }
     }
 
-    private void assignOrderBy(CriteriaQuery cq, HibernateCriteriaBuilder cb, 
Map<String, From> tablesByName) {
+    private void assignOrderBy(CriteriaQuery cq, Map<String, From> 
tablesByName) {
+        var cb = getCriteriaBuilder();
         List<Order> orders = detachedCriteria.getOrders();
         if (!orders.isEmpty()) {
             cq.orderBy(orders
@@ -643,10 +632,10 @@ public abstract class AbstractHibernateQuery extends 
Query {
         }
     }
 
-    private void assignProjections(List<Projection> projections, 
HibernateCriteriaBuilder cb, From root, CriteriaQuery cq, Map<String, From> 
tablesByName) {
+    private void assignProjections(List<Projection> projections, CriteriaQuery 
cq, Map<String, From> tablesByName) {
         List<Expression> projectionExpressions = projections
                 .stream()
-                .map(projectionToJpaExpression(cb, tablesByName))
+                .map(projectionToJpaExpression(tablesByName))
                 .filter(Objects::nonNull)
                 .map(Expression.class::cast)
                 .toList();
@@ -655,55 +644,13 @@ public abstract class AbstractHibernateQuery extends 
Query {
         } else if (projectionExpressions.size() > 1){
             cq.multiselect(projectionExpressions);
         } else {
-            cq.select(root);
-        }
-    }
-
-    private Map<String, From> assignJoinTables(List<String> joinColumns, From 
root, Map<String,DetachedAssociationCriteria> aliasMap, Map<String, From> 
fromMap) {
-        Map<String, JoinType> joinTypes = detachedCriteria.getJoinTypes();
-        //The join column is column for joining from the root entity
-        Map<String, From> tablesByName = joinColumns.stream().map(joinColumn 
-> {
-            JoinType joinType = joinTypes.entrySet()
-                    .stream()
-                    .filter(entry -> entry.getKey().equals(joinColumn))
-                    .map(Map.Entry::getValue)
-                    .findFirst()
-                    .orElse(JoinType.INNER);
-            From from = fromMap.computeIfAbsent(joinColumn, s -> 
fromMap.get("root"));
-            Join table = from.join(joinColumn, joinType);
-            String column = aliasColumn(aliasMap, joinColumn, table);
-            return new AbstractMap.SimpleEntry<>(column, table);
-        }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-        tablesByName.put("root", root);
-        return tablesByName;
-    }
-
-    private static String aliasColumn(Map<String, DetachedAssociationCriteria> 
aliasMap, String associationPath, Join table) {
-        // Attempt to find specific criteria configuration for this 
association path
-        DetachedAssociationCriteria criteria = aliasMap.get(associationPath);
-
-        if (criteria != null) {
-            // If criteria configuration exists:
-            // Determine the alias: use the one from criteria if it's not null,
-            // otherwise default back to using the associationPath itself.
-            String aliasToUse = 
Objects.requireNonNullElse(criteria.getAlias(), associationPath);
-
-            // Apply the determined alias explicitly to the Join object
-            table.alias(aliasToUse);
-
-            // Return the alias that was determined and applied
-            return aliasToUse;
-        } else {
-            // If no specific criteria configuration was found,
-            // return the original associationPath as the implicit alias.
-            // We don't explicitly call table.alias() here, letting 
JPA/Hibernate handle defaults.
-            return associationPath;
+            cq.select(tablesByName.get("root"));
         }
     }
 
     private Function<Projection, JpaExpression> projectionToJpaExpression(
-            HibernateCriteriaBuilder cb,
             Map<String, From> tablesByName) {
+        var cb = getCriteriaBuilder();
         return projection -> {
             if (countProjectionPredicate.test(projection)) {
                 return cb.count(tablesByName.get("root"));
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AliasMapEntryFunction.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AliasMapEntryFunction.java
new file mode 100644
index 0000000000..458f916c86
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AliasMapEntryFunction.java
@@ -0,0 +1,16 @@
+package org.grails.orm.hibernate.query;
+
+import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria;
+
+import java.util.Map;
+import java.util.function.Function;
+
+public class AliasMapEntryFunction
+        implements
+        Function<DetachedAssociationCriteria,
+                Map.Entry<String, DetachedAssociationCriteria>> {
+    @Override
+    public Map.Entry<String, DetachedAssociationCriteria> 
apply(DetachedAssociationCriteria detachedAssociationCriteria) {
+        return Map.entry(detachedAssociationCriteria.getAssociationPath(), 
detachedAssociationCriteria);
+    }
+}
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/DetachedAssociationFunction.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/DetachedAssociationFunction.java
new file mode 100644
index 0000000000..186d73dbe3
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/DetachedAssociationFunction.java
@@ -0,0 +1,30 @@
+package org.grails.orm.hibernate.query;
+
+import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria;
+import org.grails.datastore.mapping.query.Query;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+public class DetachedAssociationFunction implements Function<Query.Criterion, 
List<DetachedAssociationCriteria>> {
+    @Override
+    public List<DetachedAssociationCriteria> apply(Query.Criterion o) {
+        List<Query.Criterion> criteria;
+        if (o instanceof Query.In c && Objects.nonNull(c.getSubquery()) ) {
+            criteria = c.getSubquery().getCriteria();
+        } else if (o instanceof Query.Exists c && 
Objects.nonNull(c.getSubquery()) ) {
+            criteria = c.getSubquery().getCriteria();
+        } else if (o instanceof Query.NotExists c && 
Objects.nonNull(c.getSubquery()) ) {
+            criteria = c.getSubquery().getCriteria();
+        } else if (o instanceof Query.SubqueryCriterion c && 
Objects.nonNull(c.getValue()) ) {
+            criteria =  c.getValue().getCriteria();
+        } else {
+            criteria = List.of(o);
+        }
+        return criteria.stream()
+                .filter(DetachedAssociationCriteria.class::isInstance)
+                .map(DetachedAssociationCriteria.class::cast)
+                .toList();
+    }
+}
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
new file mode 100644
index 0000000000..8038900800
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
@@ -0,0 +1,105 @@
+package org.grails.orm.hibernate.query;
+
+import grails.gorm.DetachedCriteria;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.criteria.From;
+import jakarta.persistence.criteria.JoinType;
+import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria;
+import org.grails.datastore.mapping.query.Query;
+import org.hibernate.query.criteria.JpaCriteriaQuery;
+
+import java.util.AbstractMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class JpaFromProvider {
+
+    private final DetachedCriteria detachedCriteria;
+
+    public JpaFromProvider(DetachedCriteria detachedCriteria) {
+        this.detachedCriteria = detachedCriteria;
+    }
+
+    public Map<String, From> getFromsByName(JpaCriteriaQuery<?> cq, From root) 
{
+        var detachedAssociationCriteriaList = getDetachedAssociationCriteria();
+
+        var aliasMap = createAliasMap(detachedAssociationCriteriaList);
+        //The join column is column for joining from the root entity
+        var detachedFroms = createDetachedFroms(cq, 
detachedAssociationCriteriaList);
+        Map<String, From> fromsByName = 
Stream.concat(aliasMap.keySet().stream(), collectJoinColumns().stream())
+                .distinct()
+                .map(joinColumn -> {
+                    var table = detachedFroms.computeIfAbsent(joinColumn, s -> 
root).join(joinColumn, getJoinType(joinColumn));
+                    // Attempt to find specific criteria configuration for 
this association path
+                    var column = Optional.ofNullable(aliasMap.get(joinColumn))
+                            .map(detachedAssociationCriteria -> 
Objects.requireNonNullElse(detachedAssociationCriteria.getAlias(), joinColumn))
+                            .orElse(joinColumn);
+                    table.alias(column);
+                    return new AbstractMap.SimpleEntry<>(column, table);
+        }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+        fromsByName.put("root", root);
+        return fromsByName;
+    }
+
+    private JoinType getJoinType(String joinColumn) {
+        return getDetachedCriteriaJoinTypes()
+        .entrySet()
+        .stream()
+        .filter(entry -> entry.getKey().equals(joinColumn))
+        .map(Map.Entry::getValue)
+        .findFirst()
+        .orElse(JoinType.INNER);
+    }
+
+    private Map<String, From> createDetachedFroms(JpaCriteriaQuery<?> cq, 
List<DetachedAssociationCriteria> detachedAssociationCriteriaList) {
+        return detachedAssociationCriteriaList.stream()
+                .collect(Collectors.toMap(
+                        DetachedAssociationCriteria::getAssociationPath,
+                        criteria -> 
cq.from(criteria.getAssociation().getOwner().getJavaClass()) , (oldValue, 
newValue) -> newValue)
+                );
+    }
+
+    private Map<String, DetachedAssociationCriteria> 
createAliasMap(List<DetachedAssociationCriteria> 
detachedAssociationCriteriaList) {
+        return detachedAssociationCriteriaList.stream()
+                .map(new AliasMapEntryFunction())
+                .collect(Collectors.toMap(Map.Entry::getKey, 
Map.Entry::getValue));
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<Query.Criterion> getDetachedCriteria() {
+        return detachedCriteria.getCriteria();
+    }
+
+    private List<DetachedAssociationCriteria> getDetachedAssociationCriteria() 
{
+        return getDetachedCriteria()
+                .stream()
+                .map(new DetachedAssociationFunction())
+                .flatMap(List::stream)
+                .toList();
+    }
+
+    private List<String> collectJoinColumns() {
+        return getDetachedCriteriaFetchStrategies()
+                .entrySet()
+                .stream()
+                .filter(entry -> entry.getValue().equals(FetchType.EAGER))
+                .map(Map.Entry::getKey)
+                .toList();
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, JoinType> getDetachedCriteriaJoinTypes() {
+        return detachedCriteria.getJoinTypes();
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String,FetchType> getDetachedCriteriaFetchStrategies() {
+        return detachedCriteria.getFetchStrategies();
+    }
+
+
+}
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/ProjectionPredicate.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/ProjectionPredicate.java
new file mode 100644
index 0000000000..123daa609f
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/ProjectionPredicate.java
@@ -0,0 +1,47 @@
+package org.grails.orm.hibernate.query;
+
+import org.grails.datastore.mapping.query.Query;
+
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+public class ProjectionPredicate
+        implements Predicate<Query.Projection> {
+
+    @Override
+    public boolean test(Query.Projection projection) {
+        return combinePredicates(projectionPredicates).test(projection);
+    }
+
+    private final Predicate<Query.Projection> idProjectionPredicate = 
projection -> projection instanceof Query.IdProjection;
+    private final Predicate<Query.Projection> distinctProjectionPredicate = 
projection -> projection instanceof Query.DistinctProjection;
+    private final Predicate<Query.Projection> countProjectionPredicate = 
projection -> projection instanceof Query.CountProjection;
+    private final Predicate<Query.Projection> countDistinctProjection = 
projection -> projection instanceof Query.CountDistinctProjection;
+    private final Predicate<Query.Projection> maxProjectionPredicate = 
projection -> projection instanceof Query.MaxProjection;
+    private final Predicate<Query.Projection> minProjectionPredicate = 
projection -> projection instanceof Query.MinProjection;
+    private final Predicate<Query.Projection> sumProjectionPredicate = 
projection -> projection instanceof Query.SumProjection;
+    private final Predicate<Query.Projection> avgProjectionPredicate = 
projection -> projection instanceof Query.AvgProjection;
+    private final Predicate<Query.Projection> propertyProjectionPredicate = 
projection -> projection instanceof Query.PropertyProjection;
+
+    @SuppressWarnings("unchecked")
+    Predicate<Query.Projection>[] projectionPredicates = new Predicate[] {
+            idProjectionPredicate
+            , propertyProjectionPredicate
+            , countProjectionPredicate
+            , countDistinctProjection
+            , maxProjectionPredicate
+            , minProjectionPredicate
+            , sumProjectionPredicate
+            , avgProjectionPredicate
+            , distinctProjectionPredicate
+    } ;
+
+    @SafeVarargs
+    private static <T> Predicate<T> combinePredicates(Predicate<T>... 
predicates) {
+        return Arrays.stream(predicates)
+                .reduce(Predicate::or)
+                .orElse(x -> true);
+    }
+
+
+}

Reply via email to