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 50470c40921f1eb439010ce49d8e76b0fb01c4c9
Author: Walter Duque de Estrada <wbdu...@mac.com>
AuthorDate: Sat Aug 9 14:18:01 2025 -0500

    Refactoring for reuse
---
 .../AbstractHibernateGormStaticApi.groovy          |  21 +-
 .../query/AbstractHibernateCriteriaBuilder.java    |   6 +-
 .../hibernate/query/AbstractHibernateQuery.java    | 212 +++------------------
 .../grails/orm/hibernate/query/HibernateQuery.java |   4 +-
 .../hibernate/query/HibernateQueryExecutor.java    |  67 +++++++
 .../hibernate/query/JpaCriteriaQueryCreator.java   | 179 +++++++++++++++++
 6 files changed, 295 insertions(+), 194 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy
index 412d998ea4..ce913dd186 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy
@@ -4,6 +4,9 @@ import groovy.transform.CompileDynamic
 import groovy.transform.CompileStatic
 import groovy.util.logging.Slf4j
 import jakarta.persistence.NoResultException
+import jakarta.persistence.criteria.Expression
+import jakarta.persistence.criteria.Root
+import org.grails.datastore.mapping.proxy.ProxyHandler
 import org.grails.datastore.mapping.reflect.ClassUtils
 import org.grails.orm.hibernate.cfg.AbstractGrailsDomainBinder
 import org.grails.orm.hibernate.cfg.CompositeIdentity
@@ -18,6 +21,7 @@ import org.grails.datastore.gorm.finders.FinderMethod
 import org.hibernate.FlushMode
 import org.hibernate.NonUniqueResultException
 import org.hibernate.Session
+import org.hibernate.jpa.QueryHints
 import org.hibernate.query.NativeQuery
 import org.hibernate.query.Query
 import org.hibernate.query.criteria.JpaPredicate
@@ -41,6 +45,7 @@ abstract class AbstractHibernateGormStaticApi<D> extends 
GormStaticApi<D> {
     protected GrailsHibernateTemplate hibernateTemplate
     protected ConversionService conversionService
     protected final HibernateSession hibernateSession
+    ProxyHandler proxyHandler
 
 //    AbstractHibernateGormStaticApi(
 //            Class<D> persistentClass,
@@ -57,6 +62,7 @@ abstract class AbstractHibernateGormStaticApi<D> extends 
GormStaticApi<D> {
         super(persistentClass, datastore, finders, transactionManager)
         this.hibernateTemplate = new 
GrailsHibernateTemplate(datastore.getSessionFactory(), datastore)
         this.conversionService = datastore.mappingContext.conversionService
+        this.proxyHandler = datastore.mappingContext.proxyHandler
         this.hibernateSession = new HibernateSession(
                 (HibernateDatastore)datastore,
                 hibernateTemplate.getSessionFactory(),
@@ -121,9 +127,22 @@ abstract class AbstractHibernateGormStaticApi<D> extends 
GormStaticApi<D> {
         if (id == null) {
             return null
         }
+
         
         (D)hibernateTemplate.execute(  { Session session ->
-            return new HibernateQuery(hibernateSession,persistentEntity 
).idEq(id).singleResult()
+            CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder()
+            CriteriaQuery criteriaQuery = 
criteriaBuilder.createQuery(persistentEntity.javaClass)
+
+            Root queryRoot = criteriaQuery.from(persistentEntity.javaClass)
+            criteriaQuery = criteriaQuery.where (
+                    //TODO: Remove explicit type cast once GROOVY-9460
+                    criteriaBuilder.equal((Expression<?>)  
queryRoot.get(persistentEntity.identity.name), id)
+            )
+            Query criteria = session.createQuery(criteriaQuery)
+                    .setHint(QueryHints.HINT_READONLY, true)
+            HibernateHqlQuery hibernateHqlQuery = new HibernateHqlQuery(
+                    hibernateSession, persistentEntity, criteria)
+            return proxyHandler.unwrap( hibernateHqlQuery.singleResult() )
 
         } )
     }
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateCriteriaBuilder.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateCriteriaBuilder.java
index a6084ed5d2..9d19219278 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateCriteriaBuilder.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateCriteriaBuilder.java
@@ -1176,9 +1176,11 @@ public abstract class AbstractHibernateCriteriaBuilder 
extends GroovyObjectSuppo
                 if (distinct) {
                     hibernateQuery.distinct();
                     result = hibernateQuery.list();
-                } else if (scroll) {
-                    result = hibernateQuery.scroll();
                 }
+
+//                else if (scroll) {
+//                    result = hibernateQuery.scroll();
+//                }
                 else if (count) {
                     hibernateQuery.projections().count();
                     result = hibernateQuery.singleResult();
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 516e47c2d1..9fa6a32f85 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
@@ -16,13 +16,8 @@ package org.grails.orm.hibernate.query;
 
 import grails.gorm.DetachedCriteria;
 import groovy.lang.Closure;
-import jakarta.persistence.FetchType;
 import jakarta.persistence.criteria.CriteriaQuery;
-import jakarta.persistence.criteria.Expression;
-import jakarta.persistence.criteria.From;
 import jakarta.persistence.criteria.JoinType;
-import jakarta.persistence.criteria.Path;
-import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria;
 import org.grails.datastore.mapping.model.PersistentEntity;
 import org.grails.datastore.mapping.model.PersistentProperty;
 import org.grails.datastore.mapping.model.types.Association;
@@ -34,14 +29,11 @@ import 
org.grails.datastore.mapping.query.api.QueryableCriteria;
 import org.grails.orm.hibernate.AbstractHibernateSession;
 import org.grails.orm.hibernate.IHibernateTemplate;
 import org.hibernate.FlushMode;
-import org.hibernate.NonUniqueResultException;
+import org.hibernate.Session;
 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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.core.convert.support.DefaultConversionService;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -50,11 +42,7 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.function.Predicate;
 
 /**
  * Bridges the Query API with the Hibernate Criteria API
@@ -65,7 +53,6 @@ import java.util.function.Predicate;
 @SuppressWarnings("rawtypes")
 //@Slf4j
 public abstract class AbstractHibernateQuery extends Query {
-    private static final Logger LOG = 
LoggerFactory.getLogger(AbstractHibernateQuery.class);
     public static final String SIZE_CONSTRAINT_PREFIX = "Size";
 
     protected static final String ALIAS = "_alias";
@@ -440,7 +427,19 @@ public abstract class AbstractHibernateQuery extends Query 
{
 
     @Override
     public List list() {
-        return createQuery().getResultList();
+        return getHibernateQueryExecutor().list(getCurrentSession(), 
getJpaCriteriaQuery());
+    }
+
+    public List list(Session session) {
+        return getHibernateQueryExecutor().list(session, 
getJpaCriteriaQuery());
+    }
+
+    private HibernateQueryExecutor getHibernateQueryExecutor() {
+        return new HibernateQueryExecutor(offset, max, lockResult, queryCache, 
fetchSize, timeout, flushMode, readOnly, resultTransformer, proxyHandler);
+    }
+
+    public JpaCriteriaQuery<?> getJpaCriteriaQuery() {
+        return new JpaCriteriaQueryCreator(projections, getCriteriaBuilder(), 
entity, detachedCriteria).createQuery();
     }
 
 
@@ -456,190 +455,26 @@ public abstract class AbstractHibernateQuery extends 
Query {
 
     @Override
     public Object singleResult() {
-        org.hibernate.query.Query query = createQuery();
-        try {
-
-            Object singleResult = query.getSingleResult();
-            return proxyHandler.unwrap(singleResult);
-        }
-        catch (NonUniqueResultException e) {
-            return proxyHandler.unwrap(query.getResultList().get(0));
-        }
-        catch (jakarta.persistence.NoResultException e) {
-            return null;
-        }
+        return getHibernateQueryExecutor().singleResult(getCurrentSession(), 
getJpaCriteriaQuery());
     }
 
-
-    protected org.hibernate.query.Query createQuery() {
-
-        var projectionList = collectProjections();
-        var cq = createCriteriaQuery(projectionList);
-        From root = cq.from(entity.getJavaClass());
-        var tablesByName = new JpaFromProvider(detachedCriteria,cq,root);
-
-
-        assignProjections(projectionList, cq, tablesByName);
-
-        List<GroupPropertyProjection> groupProjections = 
collectGroupProjections();
-        assignGroupBy(groupProjections, root, cq, tablesByName);
-        var cb = getCriteriaBuilder();
-        assignOrderBy(cq, tablesByName);
-        assignCriteria(cq, cb, root,tablesByName);
-
-        var query = createQuery(cq);
-        Optional.ofNullable(offset).ifPresent(query::setFirstResult);
-        query.setHint("org.hibernate.cacheable", queryCache);
-        Optional.ofNullable(max).ifPresent(query::setMaxResults);
-        Optional.ofNullable(lockResult).ifPresent(query::setLockMode);
-        
Optional.ofNullable(resultTransformer).ifPresent(query::setResultTransformer);
-        Optional.ofNullable(fetchSize).ifPresent(query::setFetchSize);
-        Optional.ofNullable(timeout).ifPresent(query::setTimeout);
-        Optional.ofNullable(flushMode).map(mode -> 
mode.toJpaFlushMode()).ifPresent(query::setFlushMode);
-        Optional.ofNullable(readOnly).ifPresent(query::setReadOnly);
-        return query;
+    public Object singleResult(Session session) {
+        return getHibernateQueryExecutor().singleResult(session, 
getJpaCriteriaQuery());
     }
 
-    public org.hibernate.query.Query<?> createQuery(JpaCriteriaQuery<?> cq) {
-        return getSessionFactory()
-                .getCurrentSession()
-                .createQuery(cq);
+    public Object scroll() {
+        return getHibernateQueryExecutor().scroll(getCurrentSession(), 
getJpaCriteriaQuery());
     }
 
-
-    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;
+    public Object scroll(Session session) {
+        return getHibernateQueryExecutor().scroll(session, 
getJpaCriteriaQuery());
     }
 
-    @SuppressWarnings("unchecked")
-    private List<Query.Criterion> getDetachedCriteria() {
-        return detachedCriteria.getCriteria();
-    }
-
-
 
-    private List<GroupPropertyProjection> collectGroupProjections() {
-        List<GroupPropertyProjection> groupProjections = 
projections().getProjectionList()
-                .stream()
-                .filter(GroupPropertyProjection.class::isInstance)
-                .map(GroupPropertyProjection.class::cast)
-                .toList();
-        return groupProjections;
-    }
-
-    private List<Projection> collectProjections() {
-        return projections().getProjectionList()
-                .stream()
-                .filter(new ProjectionPredicate())
-                .toList();
-    }
-
-    private void assignCriteria(CriteriaQuery cq, HibernateCriteriaBuilder cb, 
From root, JpaFromProvider tablesByName) {
-        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, JpaFromProvider tablesByName) 
{
-        var cb = getCriteriaBuilder();
-        List<Order> orders = detachedCriteria.getOrders();
-        if (!orders.isEmpty()) {
-            cq.orderBy(orders
-                    .stream()
-                    .map(order -> {
-                        Path expression = 
tablesByName.getFullyQualifiedPath(order.getProperty());
-                        if (order.isIgnoreCase() && 
expression.getJavaType().equals(String.class)) {
-                            if 
(order.getDirection().equals(Order.Direction.ASC)) {
-                                return cb.asc(cb.lower(expression));
-                            }  else {
-                                return cb.desc(cb.lower(expression));
-                            }
-                        } else {
-                            if 
(order.getDirection().equals(Order.Direction.ASC)) {
-                                return cb.asc(expression);
-                            }  else {
-                                return cb.desc(expression);
-                            }
-                        }
-
-                    })
-                    .toList()
-            );
-        }
+    private Session getCurrentSession() {
+        return getSessionFactory().getCurrentSession();
     }
 
-    private void assignGroupBy(List<GroupPropertyProjection> groupProjections, 
From root, CriteriaQuery cq, JpaFromProvider tablesByName) {
-        if (!groupProjections.isEmpty()) {
-            List<Expression> groupByPaths = groupProjections
-                    .stream()
-                    .map(groupPropertyProjection -> {
-                        String propertyName = 
groupPropertyProjection.getPropertyName();
-                        return 
tablesByName.getFullyQualifiedPath(propertyName);
-                    })
-                    .map(Expression.class::cast)
-                    .toList();
-            cq.groupBy(groupByPaths);
-        }
-    }
-
-    private void assignProjections(List<Projection> projections, CriteriaQuery 
cq, JpaFromProvider tablesByName) {
-         var projectionExpressions = projections
-                .stream()
-                .map(projectionToJpaExpression(tablesByName))
-                .filter(Objects::nonNull)
-                .map(Expression.class::cast)
-                .toList();
-        if (projectionExpressions.size() == 1) {
-            cq.select(projectionExpressions.get(0));
-        } else if (projectionExpressions.size() > 1){
-            cq.multiselect(projectionExpressions);
-        } else {
-            cq.select(tablesByName.getFullyQualifiedPath("root"));
-        }
-    }
-
-    private Function<Projection, JpaExpression> projectionToJpaExpression(
-            JpaFromProvider tablesByName) {
-        var cb = getCriteriaBuilder();
-        return projection -> {
-            if (projection instanceof CountProjection) {
-                return cb.count(tablesByName.getFullyQualifiedPath("root"));
-            } else if (projection instanceof CountDistinctProjection 
countDistinctProjection) {
-                var propertyName = countDistinctProjection.getPropertyName();
-                return 
cb.countDistinct(tablesByName.getFullyQualifiedPath("root." + propertyName));
-            } else if (projection instanceof IdProjection) {
-                return (JpaExpression) 
tablesByName.getFullyQualifiedPath("root.id");
-            } else if (projection instanceof DistinctProjection) {
-                return null;
-            } else {
-                var propertyName = ((PropertyProjection) 
projection).getPropertyName();
-                Path path = tablesByName.getFullyQualifiedPath(propertyName);
-                if (projection instanceof MaxProjection) {
-                    return cb.max(path);
-                } else if (projection instanceof MinProjection) {
-                    return cb.min(path);
-                } else if (projection instanceof AvgProjection) {
-                    return cb.avg(path);
-                } else if (projection instanceof SumProjection) {
-                    return cb.sum(path);
-                } else { // keep this last!!!
-                    return (JpaExpression)path;
-                }
-            }
-        };
-    }
 
     private SessionFactory getSessionFactory() {
         return ((IHibernateTemplate) 
session.getNativeInterface()).getSessionFactory();
@@ -651,6 +486,7 @@ public abstract class AbstractHibernateQuery extends Query {
 
 
     @Override
+    //TODO replace this
     protected List executeQuery(PersistentEntity entity, Junction criteria) {
         return list();
     }
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java
index a413863030..bb45a7f306 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java
@@ -197,9 +197,7 @@ public class HibernateQuery extends AbstractHibernateQuery {
     }
 
 
-    public Object scroll() {
-        return createQuery().scroll();
-    }
+
 
     public Query distinct() {
         projections.add(Projections.distinct());
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQueryExecutor.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQueryExecutor.java
new file mode 100644
index 0000000000..0a58a49c03
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQueryExecutor.java
@@ -0,0 +1,67 @@
+package org.grails.orm.hibernate.query;
+
+import jakarta.persistence.LockModeType;
+import org.grails.datastore.mapping.proxy.ProxyHandler;
+import org.hibernate.FlushMode;
+import org.hibernate.NonUniqueResultException;
+import org.hibernate.Session;
+import org.hibernate.query.Query;
+import org.hibernate.query.ResultListTransformer;
+import org.hibernate.query.criteria.JpaCriteriaQuery;
+import org.hibernate.transform.ResultTransformer;
+
+import java.util.List;
+import java.util.Optional;
+
+public record HibernateQueryExecutor(
+        Integer offset
+        , Integer maxResults
+        , LockModeType lockResult
+        , Boolean queryCache
+        , Integer fetchSize
+        , Integer timeout
+        , FlushMode flushMode
+        , Boolean readOnly
+        , ResultTransformer resultTransformer
+        , ProxyHandler proxyHandler
+
+) {
+    public List list(Session session, JpaCriteriaQuery jpaCq) {
+        return configureQuery(session, jpaCq).getResultList();
+    }
+
+    public Object scroll(Session session, JpaCriteriaQuery jpaCq) {
+        return configureQuery(session, jpaCq).scroll();
+    }
+
+    public Object singleResult(Session session, JpaCriteriaQuery jpaCq) {
+        var query = configureQuery(session, jpaCq);
+        try {
+
+            Object singleResult = query.getSingleResult();
+            return proxyHandler.unwrap(singleResult);
+        }
+        catch (NonUniqueResultException e) {
+            return proxyHandler.unwrap(query.getResultList().get(0));
+        }
+        catch (jakarta.persistence.NoResultException e) {
+            return null;
+        }
+    }
+
+  private Query configureQuery(Session session, JpaCriteriaQuery jpaCq) {
+      var query = session.createQuery(jpaCq);
+      Optional.ofNullable(offset).ifPresent(query::setFirstResult);
+      Optional.ofNullable(queryCache).ifPresent( qc -> 
query.setHint("org.hibernate.cacheable", qc));
+      Optional.ofNullable(maxResults).ifPresent(query::setMaxResults);
+      Optional.ofNullable(lockResult).ifPresent(query::setLockMode);
+      
Optional.ofNullable(resultTransformer).ifPresent(query::setResultTransformer);
+      Optional.ofNullable(fetchSize).ifPresent(query::setFetchSize);
+      Optional.ofNullable(timeout).ifPresent(query::setTimeout);
+      Optional.ofNullable(flushMode).map(mode -> 
mode.toJpaFlushMode()).ifPresent(query::setFlushMode);
+      Optional.ofNullable(readOnly).ifPresent(query::setReadOnly);
+      return query;
+
+  }
+
+}
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java
new file mode 100644
index 0000000000..b2268c9b12
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java
@@ -0,0 +1,179 @@
+package org.grails.orm.hibernate.query;
+
+import grails.gorm.DetachedCriteria;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.From;
+import jakarta.persistence.criteria.Path;
+import org.grails.datastore.mapping.model.PersistentEntity;
+import org.grails.datastore.mapping.query.Query;
+import org.hibernate.query.criteria.HibernateCriteriaBuilder;
+import org.hibernate.query.criteria.JpaCriteriaQuery;
+import org.hibernate.query.criteria.JpaExpression;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+public class JpaCriteriaQueryCreator {
+
+    private final Query.ProjectionList projections;
+    private final HibernateCriteriaBuilder criteriaBuilder;
+    private final PersistentEntity entity;
+    private final DetachedCriteria detachedCriteria;
+
+    public JpaCriteriaQueryCreator(
+            Query.ProjectionList projections
+            , HibernateCriteriaBuilder criteriaBuilder
+            , PersistentEntity entity
+            , DetachedCriteria detachedCriteria
+    ) {
+        this.projections = projections;
+        this.criteriaBuilder = criteriaBuilder;
+        this.entity = entity;
+        this.detachedCriteria = detachedCriteria;
+    }
+
+    public JpaCriteriaQuery<?> createQuery() {
+
+        var projectionList = collectProjections();
+        var cq = createCriteriaQuery(projectionList);
+        From root = cq.from(entity.getJavaClass());
+        var tablesByName = new JpaFromProvider(detachedCriteria,cq,root);
+
+
+        assignProjections(projectionList, cq, tablesByName);
+
+        List<Query.GroupPropertyProjection> groupProjections = 
collectGroupProjections();
+        assignGroupBy(groupProjections, root, cq, tablesByName);
+
+        assignOrderBy(cq, tablesByName);
+        assignCriteria(cq, root,tablesByName);
+        return cq;
+    }
+
+    private List<Query.Projection> collectProjections() {
+        return projections.getProjectionList()
+                .stream()
+                .filter(new ProjectionPredicate())
+                .toList();
+    }
+
+    private JpaCriteriaQuery<?> createCriteriaQuery(List<Query.Projection> 
projections) {
+        var cq = projections.stream()
+                .filter( it -> !(it instanceof Query.DistinctProjection || it 
instanceof Query.DistinctPropertyProjection))
+                .toList().size() > 1 ?  
criteriaBuilder.createQuery(Object[].class) : 
criteriaBuilder.createQuery(Object.class);
+        projections.stream()
+                .filter( it -> it instanceof Query.DistinctProjection || it 
instanceof Query.DistinctPropertyProjection)
+                .findFirst()
+                .ifPresent(projection -> {
+                    cq.distinct(true);
+                });
+        return cq;
+    }
+
+    private void assignProjections(List<Query.Projection> projections, 
CriteriaQuery cq, JpaFromProvider tablesByName) {
+        var projectionExpressions = projections
+                .stream()
+                .map(projectionToJpaExpression(tablesByName))
+                .filter(Objects::nonNull)
+                .map(Expression.class::cast)
+                .toList();
+        if (projectionExpressions.size() == 1) {
+            cq.select(projectionExpressions.get(0));
+        } else if (projectionExpressions.size() > 1){
+            cq.multiselect(projectionExpressions);
+        } else {
+            cq.select(tablesByName.getFullyQualifiedPath("root"));
+        }
+    }
+
+    private void assignGroupBy(List<Query.GroupPropertyProjection> 
groupProjections, From root, CriteriaQuery cq, JpaFromProvider tablesByName) {
+        if (!groupProjections.isEmpty()) {
+            List<Expression> groupByPaths = groupProjections
+                    .stream()
+                    .map(groupPropertyProjection -> {
+                        String propertyName = 
groupPropertyProjection.getPropertyName();
+                        return 
tablesByName.getFullyQualifiedPath(propertyName);
+                    })
+                    .map(Expression.class::cast)
+                    .toList();
+            cq.groupBy(groupByPaths);
+        }
+    }
+
+    private void assignOrderBy(CriteriaQuery cq, JpaFromProvider tablesByName) 
{
+        List<Query.Order> orders = detachedCriteria.getOrders();
+        if (!orders.isEmpty()) {
+            cq.orderBy(orders
+                    .stream()
+                    .map(order -> {
+                        Path expression = 
tablesByName.getFullyQualifiedPath(order.getProperty());
+                        if (order.isIgnoreCase() && 
expression.getJavaType().equals(String.class)) {
+                            if 
(order.getDirection().equals(Query.Order.Direction.ASC)) {
+                                return 
criteriaBuilder.asc(criteriaBuilder.lower(expression));
+                            }  else {
+                                return 
criteriaBuilder.desc(criteriaBuilder.lower(expression));
+                            }
+                        } else {
+                            if 
(order.getDirection().equals(Query.Order.Direction.ASC)) {
+                                return criteriaBuilder.asc(expression);
+                            }  else {
+                                return criteriaBuilder.desc(expression);
+                            }
+                        }
+
+                    })
+                    .toList()
+            );
+        }
+    }
+
+    private void assignCriteria(CriteriaQuery cq ,From root, JpaFromProvider 
tablesByName) {
+        List<Query.Criterion>  criteriaList =detachedCriteria.getCriteria();
+        if (!criteriaList.isEmpty()) {
+            jakarta.persistence.criteria.Predicate[] predicates = 
PredicateGenerator.getPredicates(criteriaBuilder, cq, root, criteriaList, 
tablesByName);
+            cq.where(criteriaBuilder.and(predicates));
+        }
+    }
+
+    private Function<Query.Projection, JpaExpression> 
projectionToJpaExpression(
+            JpaFromProvider tablesByName) {
+        return projection -> {
+            if (projection instanceof Query.CountProjection) {
+                return 
criteriaBuilder.count(tablesByName.getFullyQualifiedPath("root"));
+            } else if (projection instanceof Query.CountDistinctProjection 
countDistinctProjection) {
+                var propertyName = countDistinctProjection.getPropertyName();
+                return 
criteriaBuilder.countDistinct(tablesByName.getFullyQualifiedPath("root." + 
propertyName));
+            } else if (projection instanceof Query.IdProjection) {
+                return (JpaExpression) 
tablesByName.getFullyQualifiedPath("root.id");
+            } else if (projection instanceof Query.DistinctProjection) {
+                return null;
+            } else {
+                var propertyName = ((Query.PropertyProjection) 
projection).getPropertyName();
+                Path path = tablesByName.getFullyQualifiedPath(propertyName);
+                if (projection instanceof Query.MaxProjection) {
+                    return criteriaBuilder.max(path);
+                } else if (projection instanceof Query.MinProjection) {
+                    return criteriaBuilder.min(path);
+                } else if (projection instanceof Query.AvgProjection) {
+                    return criteriaBuilder.avg(path);
+                } else if (projection instanceof Query.SumProjection) {
+                    return criteriaBuilder.sum(path);
+                } else { // keep this last!!!
+                    return (JpaExpression)path;
+                }
+            }
+        };
+    }
+
+    private List<Query.GroupPropertyProjection> collectGroupProjections() {
+        List<Query.GroupPropertyProjection> groupProjections = 
projections.getProjectionList()
+                .stream()
+                .filter(Query.GroupPropertyProjection.class::isInstance)
+                .map(Query.GroupPropertyProjection.class::cast)
+                .toList();
+        return groupProjections;
+    }
+
+}

Reply via email to