This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit d59b49ff3ced99bd08b7106086801677d8ba88db Author: Walter Duque de Estrada <[email protected]> AuthorDate: Fri Feb 20 07:53:47 2026 -0600 Implement JpaCriteriaQueryCreatorSpec and fix distinct projection bug in Query.java --- .../grails/orm/hibernate/query/HibernateQuery.java | 25 +-- .../hibernate/query/JpaCriteriaQueryCreator.java | 2 +- .../specs/hibernatequery/HibernateQuerySpec.groovy | 11 ++ .../JpaCriteriaQueryCreatorSpec.groovy | 193 +++++++++++++++++++++ .../org/grails/datastore/mapping/query/Query.java | 1 + 5 files changed, 209 insertions(+), 23 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java index 48b2a5f956..4f7cf8dd2d 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateQuery.java @@ -37,15 +37,12 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaQuery; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.dao.InvalidDataAccessApiUsageException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * Bridges the Query API with the Hibernate Criteria API @@ -55,27 +52,16 @@ import java.util.concurrent.ConcurrentHashMap; */ @SuppressWarnings("rawtypes") public class HibernateQuery extends Query { - - public static final String SIZE_CONSTRAINT_PREFIX = "Size"; - protected static final String ALIAS = "_alias"; - protected static ConversionService conversionService = new DefaultConversionService(); - - private static final Map<String, Boolean> JOIN_STATUS_CACHE = new ConcurrentHashMap<String, Boolean>(); - protected String alias; protected int aliasCount; - public DetachedCriteria getDetachedCriteria() { return detachedCriteria; } - - protected Map<String, CriteriaAndAlias> createdAssociationPaths = new HashMap<String, CriteriaAndAlias>(); - protected LinkedList<String> aliasStack = new LinkedList<String>(); + protected Map<String, CriteriaAndAlias> createdAssociationPaths = new HashMap<>(); + protected LinkedList<String> aliasStack = new LinkedList<>(); protected LinkedList<PersistentEntity> entityStack = new LinkedList<PersistentEntity>(); protected LinkedList<Association> associationStack = new LinkedList<Association>(); - protected LinkedList aliasInstanceStack = new LinkedList(); - private boolean hasJoins = false; protected DetachedCriteria detachedCriteria; protected ProxyHandler proxyHandler = new HibernateProxyHandler(); protected PredicateGenerator predicateGenerator; @@ -94,9 +80,6 @@ public class HibernateQuery extends Query { this.detachedCriteria = detachedCriteria; } - - - @Override protected Object resolveIdIfEntity(Object value) { // for Hibernate queries, the object itself is used in queries, not the id @@ -321,11 +304,9 @@ public class HibernateQuery extends Query { @Override public AssociationQuery createQuery(String associationName) { final PersistentProperty property = entity.getPropertyByName(calculatePropertyName(associationName)); - if ((property instanceof Association)) { + if ((property instanceof Association association)) { String alias = generateAlias(associationName); CriteriaAndAlias subCriteria = getOrCreateAlias(associationName, alias); - - Association association = (Association) property; if(subCriteria.criteria != null) { return new HibernateAssociationQuery(subCriteria.criteria, (AbstractHibernateSession) getSession(), association.getAssociatedEntity(), association, alias); } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java index c6ca7d0fc8..3b970c44fa 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaCriteriaQueryCreator.java @@ -41,7 +41,7 @@ public class JpaCriteriaQueryCreator { var projectionList = collectProjections(); var cq = createCriteriaQuery(projectionList); - From root = cq.from(entity.getJavaClass()); + var root = cq.from(entity.getJavaClass()); var tablesByName = new JpaFromProvider(detachedCriteria,cq,root); diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy index 1126a551b4..d3dd27835c 100644 --- a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy @@ -900,6 +900,17 @@ class HibernateQuerySpec extends HibernateGormDatastoreSpec { results[0] == "Bob" } + + def distinctQuery() { + given: + new Person(firstName: "Bob", lastName: "Builder", age: 50).save(flush: true) + hibernateQuery.select("firstName").distinct() + when: + def results = hibernateQuery.list() + then: + results.size() == 1 + results[0] == "Bob" + } } diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaCriteriaQueryCreatorSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaCriteriaQueryCreatorSpec.groovy new file mode 100644 index 0000000000..9800ec8bfe --- /dev/null +++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/hibernatequery/JpaCriteriaQueryCreatorSpec.groovy @@ -0,0 +1,193 @@ +package grails.gorm.specs.hibernatequery + +import grails.gorm.DetachedCriteria +import grails.gorm.specs.HibernateGormDatastoreSpec +import org.apache.grails.data.testing.tck.domains.Person +import org.grails.datastore.mapping.query.Query +import org.grails.orm.hibernate.query.JpaCriteriaQueryCreator +import org.grails.orm.hibernate.query.PredicateGenerator +import org.hibernate.query.criteria.HibernateCriteriaBuilder +import org.hibernate.query.criteria.JpaCriteriaQuery + +class JpaCriteriaQueryCreatorSpec extends HibernateGormDatastoreSpec { + + def setupSpec() { + manager.addAllDomainClasses([Person]) + } + + def "test createQuery with property projection"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + var projections = new Query.ProjectionList() + projections.property("firstName") + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + query.getSelection() != null + } + + def "test createQuery with multiple projections"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + var projections = new Query.ProjectionList() + projections.property("firstName") + projections.property("lastName") + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + query.getSelection() != null + } + + def "test createQuery with count projection"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + var projections = new Query.ProjectionList() + projections.count() + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + } + + def "test createQuery with countDistinct projection"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + var projections = new Query.ProjectionList() + projections.countDistinct("firstName") + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + } + + def "test createQuery with id projection"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + var projections = new Query.ProjectionList() + projections.id() + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + } + + def "test createQuery with aggregate projections"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + var projections = new Query.ProjectionList() + projections.max("age") + projections.min("age") + projections.avg("age") + projections.sum("age") + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + } + + def "test createQuery with groupProperty and order"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + detachedCriteria.order(Query.Order.asc("lastName")) + detachedCriteria.order(new Query.Order("firstName", Query.Order.Direction.DESC).ignoreCase()) + + var projections = new Query.ProjectionList() + projections.groupProperty("lastName") + projections.count() + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + } + + def "test createQuery with criteria"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + detachedCriteria.eq("firstName", "Bob") + + var projections = new Query.ProjectionList() + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + } + + def "test createQuery with distinct"() { + given: + HibernateCriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder() + var entity = manager.hibernateDatastore.getMappingContext().getPersistentEntity(Person.typeName) + var detachedCriteria = new DetachedCriteria(Person) + + var projections = new Query.ProjectionList() + projections.distinct() + projections.property("firstName") + var predicateGenerator = new PredicateGenerator() + + var creator = new JpaCriteriaQueryCreator(projections, criteriaBuilder, entity, detachedCriteria, predicateGenerator) + + when: + JpaCriteriaQuery<?> query = creator.createQuery() + + then: + query != null + query.isDistinct() + } +} diff --git a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java index 3aa7fb603e..dc83ea6e19 100644 --- a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java +++ b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/query/Query.java @@ -1520,6 +1520,7 @@ public abstract class Query implements Cloneable { } public ProjectionList distinct() { + add(Projections.distinct()); return this; }
