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; + } + +}