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

Reply via email to