diff --git a/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy b/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy index 79cf0957..9c4f5ac7 100644 --- a/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy +++ b/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateGormStaticApi.groovy @@ -173,7 +173,7 @@ abstract class AbstractHibernateGormStaticApi extends GormStaticApi { Integer count() { (Integer)hibernateTemplate.execute({ Session session -> CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder() - CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(persistentEntity.javaClass) + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Long.class) criteriaQuery.select(criteriaBuilder.count(criteriaQuery.from(persistentEntity.javaClass))) Query criteria = session.createQuery(criteriaQuery) HibernateHqlQuery hibernateHqlQuery = new HibernateHqlQuery( @@ -583,9 +583,9 @@ abstract class AbstractHibernateGormStaticApi extends GormStaticApi { CriteriaQuery cq = cb.createQuery(persistentEntity.javaClass) def root = cq.from(persistentEntity.javaClass) def listOfPredicates = queryArgs.collect { entry -> cb.equal(root.get(entry.key), entry.value) } - def nullPredicates = nullNames.collect { nullName -> root.get(nullName).isNull() } + def nullPredicates = nullNames.collect { nullName -> cb.isNotNull(root.get(nullName))} def jpaPredicates = (listOfPredicates + nullPredicates).toArray(new JpaPredicate[0]) - cq.where(cb.and(jpaPredicates)) + cq.select(root).where(cb.and(jpaPredicates)) firePreQueryEvent(session, cq) List results = session.createQuery(cq).resultList firePostQueryEvent(session, cq, results) @@ -654,9 +654,9 @@ abstract class AbstractHibernateGormStaticApi extends GormStaticApi { CriteriaQuery cq = cb.createQuery(persistentEntity.javaClass) def root = cq.from(persistentEntity.javaClass) def listOfPredicates = queryArgs.collect { entry -> cb.equal(root.get(entry.key), entry.value) } - def nullPredicates = nullNames.collect { nullName -> root.get(nullName).isNull() } + def nullPredicates = nullNames.collect { nullName -> cb.isNotNull(root.get(nullName)) } JpaPredicate[] jpaPredicates = (listOfPredicates + nullPredicates).toArray(new JpaPredicate[0]) - cq.where(cb.and(jpaPredicates)) + cq.select(root).where(cb.and(jpaPredicates)) firePreQueryEvent(session, cq) Object result = session.createQuery(cq).singleResult firePostQueryEvent(session, cq, result) diff --git a/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java b/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java index 55f3b02f..c8b1e5d5 100644 --- a/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java +++ b/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/AbstractHibernateQuery.java @@ -16,50 +16,33 @@ import jakarta.persistence.FetchType; import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Root; -import org.grails.datastore.gorm.finders.DynamicFinder; import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria; -import org.grails.datastore.mapping.core.Datastore; import org.grails.datastore.mapping.model.PersistentEntity; import org.grails.datastore.mapping.model.PersistentProperty; import org.grails.datastore.mapping.model.types.Association; -import org.grails.datastore.mapping.model.types.Embedded; -import org.grails.datastore.mapping.proxy.ProxyHandler; import org.grails.datastore.mapping.query.AssociationQuery; import org.grails.datastore.mapping.query.Query; -import org.grails.datastore.mapping.query.api.QueryableCriteria; -import org.grails.datastore.mapping.query.criteria.FunctionCallingCriterion; -import org.grails.datastore.mapping.query.event.PostQueryEvent; -import org.grails.datastore.mapping.query.event.PreQueryEvent; import org.grails.orm.hibernate.AbstractHibernateSession; import org.grails.orm.hibernate.IHibernateTemplate; -import org.grails.orm.hibernate.cfg.AbstractGrailsDomainBinder; -import org.grails.orm.hibernate.cfg.Mapping; -import org.hibernate.FetchMode; -import org.hibernate.LockMode; -import org.hibernate.NonUniqueResultException; import org.hibernate.SessionFactory; -import org.hibernate.dialect.Dialect; import org.hibernate.persister.entity.PropertyMapping; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.sqm.tree.SqmJoinType; -import org.hibernate.type.BasicType; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.dao.InvalidDataAccessResourceUsageException; -import org.springframework.util.ReflectionUtils; -import java.lang.reflect.Field; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; /** * Bridges the Query API with the Hibernate Criteria API @@ -76,9 +59,9 @@ public abstract class AbstractHibernateQuery extends Query { protected static ConversionService conversionService = new DefaultConversionService(); private static final Map JOIN_STATUS_CACHE = new ConcurrentHashMap(); - protected final Root root; + protected Root root; - protected CriteriaQuery criteria; + protected CriteriaQuery criteriaQuery; protected AbstractHibernateQuery.HibernateProjectionList hibernateProjectionList; protected String alias; protected int aliasCount; @@ -89,10 +72,9 @@ public abstract class AbstractHibernateQuery extends Query { protected LinkedList aliasInstanceStack = new LinkedList(); private boolean hasJoins = false; - protected AbstractHibernateQuery(CriteriaQuery criteria, AbstractHibernateSession session, PersistentEntity entity) { + + protected AbstractHibernateQuery(CriteriaQuery criteriaQuery, AbstractHibernateSession session, PersistentEntity entity) { super(session, entity); - this.criteria = criteria; - this.root = criteria.from(entity.getJavaClass()); if(entity != null) { initializeJoinStatus(); } @@ -141,10 +123,7 @@ public Query isNotNull(String property) { return this; } - @Override - public void add(Criterion criterion) { - } @Override @@ -323,7 +302,7 @@ protected CriteriaAndAlias getCriteriaAndAlias(DetachedAssociationCriteria assoc protected CriteriaAndAlias getOrCreateAlias(String associationName, String alias) { CriteriaAndAlias subCriteria = null; String associationPath = getAssociationPath(associationName); - CriteriaQuery parentCriteria = criteria; + CriteriaQuery parentCriteria = criteriaQuery; if(alias == null) { alias = generateAlias(associationName); } @@ -370,14 +349,6 @@ private SqmJoinType resolveJoinType(JoinType joinType) { } } - @Override - public ProjectionList projections() { - if (hibernateProjectionList == null) { - hibernateProjectionList = new HibernateProjectionList(); - } - return hibernateProjectionList; - } - @Override public Query max(int max) { return this; @@ -412,16 +383,9 @@ public Query lock(boolean lock) { @Override public Query order(Order order) { super.order(order); - - - return this; } - - - - @Override public Query join(String property) { this.hasJoins = true; @@ -432,13 +396,15 @@ public Query join(String property) { @Override public Query select(String property) { this.hasJoins = true; - this.criteria.select(root.get(property)); + this.criteriaQuery.select(root.get(property)); return this; } @Override public List list() { - return getQuery().getResultList(); + org.hibernate.query.Query query = getQuery(); + System.out.println(query.getQueryString()); + return query.getResultList(); } @@ -453,8 +419,47 @@ public Object singleResult() { return getQuery().getSingleResult(); } + private final Predicate countProjectionPredicate = projection -> projection instanceof CountProjection; + + @SuppressWarnings("unchecked") + Predicate[] projectionPredicates = new Predicate[] {countProjectionPredicate} ; + + @SafeVarargs + private static Predicate combinePredicates(Predicate... predicates) { + return Arrays.stream(predicates) + .reduce(Predicate::and) + .orElse(x -> true); + } + private org.hibernate.query.Query getQuery() { - return ((IHibernateTemplate) session.getNativeInterface()).getSessionFactory().getCurrentSession().createQuery(criteria); + List projections = projections() + .getProjectionList() + .stream() + .filter(combinePredicates(projectionPredicates)).toList(); + if (projections.size() == 1 && projections.get(0) instanceof CountProjection) { + criteriaQuery = getCriteriaBuilder().createQuery(Long.class); + root = criteriaQuery.from(entity.getJavaClass()); + criteriaQuery.select(getCriteriaBuilder().count(root)); + } else { + criteriaQuery = getCriteriaBuilder().createQuery(entity.getJavaClass()); + root = criteriaQuery.from(entity.getJavaClass()); + criteriaQuery.select(root); + } + + List predicates = this.criteria.getCriteria().stream(). + map(criterion -> { + if (criterion instanceof IsNotNull isNotNull) { + return getCriteriaBuilder().isNotNull(root.get(isNotNull.getProperty())); + } + return null; + }).filter(Objects::nonNull).toList(); + + criteriaQuery.where(getCriteriaBuilder().and(predicates.toArray(new JpaPredicate[0]))); + return ((IHibernateTemplate) session.getNativeInterface()).getSessionFactory().getCurrentSession().createQuery(criteriaQuery); + } + + private HibernateCriteriaBuilder getCriteriaBuilder() { + return ((IHibernateTemplate) session.getNativeInterface()).getSessionFactory().getCriteriaBuilder(); } diff --git a/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java b/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java index c0aba095..4e6476af 100644 --- a/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java +++ b/grails-datastore-gorm-hibernate/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java @@ -22,7 +22,6 @@ import org.grails.datastore.mapping.model.PersistentEntity; import org.hibernate.SessionFactory; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.PropertyMapping; @@ -56,7 +55,7 @@ protected PropertyMapping getEntityPersister(String name, SessionFactory session * @return The hibernate criteria */ public CriteriaQuery getHibernateCriteria() { - return this.criteria; + return this.criteriaQuery; }