This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit c28ebf1321e6bb66f37631ff009eba1c0c1c4f7b
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Fri Mar 20 14:33:30 2026 -0500

    hibernate 7:
     1. Enables the DSL: Adds createAlias to HibernateCriteriaBuilder and the 
CriteriaMethodInvoker infrastructure.
       2. Solves the "Basic Collection" Gap: Introduces the HibernateAlias 
metadata class to handle aliasing for basic collections (like Set<String>), 
which GORM traditionally handled differently than
          standard associations.
       3. Bypasses H7 Restrictions: Updates JpaFromProvider and 
PredicateGenerator to use these aliases to resolve the "multivalued path" 
errors that Hibernate 7 throws when seeing collections in IN
          clauses.
       4. Ensures Validation: Fixes the ConfigurationException so the query 
engine recognizes these aliases as valid paths.
---
 .../grails/orm/HibernateCriteriaBuilder.java       |  4 ++--
 .../grails/orm/hibernate/query/HibernateQuery.java | 11 ++++++++++-
 .../hibernate/query/JpaCriteriaQueryCreator.java   | 18 +++++++++++++++--
 .../orm/hibernate/query/JpaFromProvider.java       | 20 +++++++++++++------
 .../orm/hibernate/query/PredicateGenerator.java    |  5 ++++-
 .../gorm/specs/BasicCollectionInQuerySpec.groovy   |  2 +-
 .../specs/hibernatequery/HibernateQuerySpec.groovy | 12 +++++++++++
 .../JpaCriteriaQueryCreatorSpec.groovy             | 23 ++++++++++++++++++++++
 8 files changed, 82 insertions(+), 13 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java
 
b/grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java
index 5d293f45cf..fd07b327d2 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java
@@ -171,7 +171,7 @@ public class HibernateCriteriaBuilder extends 
GroovyObjectSupport implements Bui
     public org.grails.datastore.mapping.query.api.Criteria createAlias(String 
associationPath, String alias) {
         var prop = 
hibernateQuery.getEntity().getPropertyByName(associationPath);
         if (prop instanceof org.grails.datastore.mapping.model.types.Basic) {
-            hibernateQuery.getDetachedCriteria().add(new 
org.grails.orm.hibernate.query.HibernateAlias(associationPath, alias));
+            hibernateQuery.addAlias(new 
org.grails.orm.hibernate.query.HibernateAlias(associationPath, alias));
             return this;
         }
         hibernateQuery.getDetachedCriteria().createAlias(associationPath, 
alias);
@@ -181,7 +181,7 @@ public class HibernateCriteriaBuilder extends 
GroovyObjectSupport implements Bui
     public org.grails.datastore.mapping.query.api.Criteria createAlias(String 
associationPath, String alias, int joinType) {
         var prop = 
hibernateQuery.getEntity().getPropertyByName(associationPath);
         if (prop instanceof org.grails.datastore.mapping.model.types.Basic) {
-            hibernateQuery.getDetachedCriteria().add(new 
org.grails.orm.hibernate.query.HibernateAlias(associationPath, alias));
+            hibernateQuery.addAlias(new 
org.grails.orm.hibernate.query.HibernateAlias(associationPath, alias));
             return this;
         }
         hibernateQuery.getDetachedCriteria().createAlias(associationPath, 
alias);
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 0c201a294e..d7a1cf2d38 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
@@ -82,6 +82,15 @@ public class HibernateQuery extends Query {
     protected Deque<Association> associationStack = new LinkedList<>();
     protected DetachedCriteria<?> detachedCriteria;
     protected ProxyHandler proxyHandler = new HibernateProxyHandler();
+    private final List<HibernateAlias> aliases = new java.util.ArrayList<>();
+
+    public List<HibernateAlias> getAliases() {
+        return Collections.unmodifiableList(aliases);
+    }
+
+    public void addAlias(HibernateAlias alias) {
+        this.aliases.add(alias);
+    }
 
     private Integer fetchSize;
     private Integer timeout;
@@ -428,7 +437,7 @@ public class HibernateQuery extends Query {
     public JpaCriteriaQuery<?> getJpaCriteriaQuery() {
         ConversionService conversionService = 
getSession().getMappingContext().getConversionService();
         return new JpaCriteriaQueryCreator(
-                        projections, getCriteriaBuilder(), entity, 
detachedCriteria, conversionService)
+                        projections, getCriteriaBuilder(), entity, 
detachedCriteria, conversionService, this)
                 .createQuery();
     }
 
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 05abe24f3b..59e5e4080a 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
@@ -51,6 +51,7 @@ public class JpaCriteriaQueryCreator {
     private final PersistentEntity entity;
     private final DetachedCriteria<?> detachedCriteria;
     private final ConversionService conversionService;
+    private final HibernateQuery hibernateQuery;
 
     public JpaCriteriaQueryCreator(
             Query.ProjectionList projections,
@@ -58,11 +59,22 @@ public class JpaCriteriaQueryCreator {
             PersistentEntity entity,
             DetachedCriteria<?> detachedCriteria,
             ConversionService conversionService) {
+        this(projections, criteriaBuilder, entity, detachedCriteria, 
conversionService, null);
+    }
+
+    public JpaCriteriaQueryCreator(
+            Query.ProjectionList projections,
+            HibernateCriteriaBuilder criteriaBuilder,
+            PersistentEntity entity,
+            DetachedCriteria<?> detachedCriteria,
+            ConversionService conversionService,
+            HibernateQuery hibernateQuery) {
         this.projections = projections;
         this.criteriaBuilder = criteriaBuilder;
         this.entity = entity;
         this.detachedCriteria = detachedCriteria;
         this.conversionService = conversionService;
+        this.hibernateQuery = hibernateQuery;
     }
 
     public JpaCriteriaQuery<?> createQuery() {
@@ -71,7 +83,8 @@ public class JpaCriteriaQueryCreator {
         var cq = createCriteriaQuery(projectionList);
         Class<?> javaClass = entity.getJavaClass();
         Root<?> root = cq.from(javaClass);
-        var tablesByName = new JpaFromProvider(detachedCriteria, 
projectionList, root);
+        var tablesByName = new JpaFromProvider(detachedCriteria, 
projectionList, 
+                hibernateQuery != null ? hibernateQuery.getAliases() : 
List.of(), root);
         assignProjections(projectionList, cq, tablesByName);
         assignGroupBy(cq, tablesByName);
 
@@ -85,7 +98,8 @@ public class JpaCriteriaQueryCreator {
         var projectionList = collectProjections();
         Class<?> javaClass = entity.getJavaClass();
         Root<?> root = subquery.from(javaClass);
-        var tablesByName = new JpaFromProvider(detachedCriteria, 
projectionList, root);
+        var tablesByName = new JpaFromProvider(detachedCriteria, 
projectionList, 
+                hibernateQuery != null ? hibernateQuery.getAliases() : 
List.of(), root);
 
         var aliasedProjections = new 
java.util.concurrent.atomic.AtomicInteger(0);
         var projectionExpressions = projectionList.stream()
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
index 58bf79ea26..d3cb2d9563 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/JpaFromProvider.java
@@ -51,11 +51,16 @@ public class JpaFromProvider implements Cloneable {
         this.fromMap = new HashMap<>(fromMap);
     }
 
+    public JpaFromProvider(DetachedCriteria<?> detachedCriteria, 
List<Query.Projection> projections, From<?, ?> root) {
+        this(detachedCriteria, projections, List.of(), root);
+    }
+
     public JpaFromProvider(
             DetachedCriteria<?> detachedCriteria,
             List<Query.Projection> projections,
+            List<HibernateAlias> aliases,
             From<?, ?> root) {
-        fromMap = getFromsByName(detachedCriteria, projections, root);
+        fromMap = getFromsByName(detachedCriteria, projections, aliases, root);
     }
 
     public JpaFromProvider(
@@ -64,16 +69,21 @@ public class JpaFromProvider implements Cloneable {
             List<Query.Projection> projections,
             From<?, ?> root) {
         fromMap = new HashMap<>(parent.fromMap);
-        fromMap.putAll(getFromsByName(detachedCriteria, projections, root));
+        fromMap.putAll(getFromsByName(detachedCriteria, projections, 
List.of(), root));
     }
 
     public Map<String, From<?, ?>> getFromsByName() {
         return fromMap;
     }
 
+    public boolean hasAlias(String name) {
+        return fromMap.containsKey(name);
+    }
+
     protected Map<String, From<?, ?>> getFromsByName(
             DetachedCriteria<?> detachedCriteria,
             List<Query.Projection> projections,
+            List<HibernateAlias> aliases,
             From<?, ?> root) {
         var detachedAssociationCriteriaList = 
detachedCriteria.getCriteria().stream()
                 .map(new DetachedAssociationFunction())
@@ -84,10 +94,8 @@ public class JpaFromProvider implements Cloneable {
         
         // Also scan for HibernateAlias (basic collections)
         Map<String, String> basicAliasMap = new HashMap<>();
-        for (Query.Criterion c : detachedCriteria.getCriteria()) {
-            if (c instanceof HibernateAlias ha) {
-                basicAliasMap.put(ha.getPath(), ha.getAlias());
-            }
+        for (HibernateAlias ha : aliases) {
+            basicAliasMap.put(ha.getPath(), ha.getAlias());
         }
 
         var definedAliases = detachedAssociationCriteriaList.stream()
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
index 16cfd1c1b3..2a4ba1f7d2 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
@@ -100,6 +100,8 @@ public class PredicateGenerator {
         } else if (criterion instanceof Query.NotExists c) {
             PersistentEntity childEntity = 
c.getSubquery().getPersistentEntity();
             return cb.not(handleExists(cb, criteriaQuery, root, 
fromsByProvider, childEntity, new Query.Exists(c.getSubquery())));
+        } else if (criterion instanceof HibernateAlias) {
+            return null; // Metadata only, handled by JpaFromProvider
         }
         throw new IllegalArgumentException("Unsupported criterion: " + 
criterion);
     }
@@ -170,7 +172,8 @@ public class PredicateGenerator {
             Query.PropertyCriterion pc) {
 
         String propertyName = pc.getProperty();
-        if (!"id".equals(propertyName) && !propertyName.contains(".") && 
entity.getPropertyByName(propertyName) == null) {
+        if (!"id".equals(propertyName) && !propertyName.contains(".") && 
+                entity.getPropertyByName(propertyName) == null && 
!fromsByProvider.hasAlias(propertyName)) {
             throw new ConfigurationException("Property [" + propertyName +
                     "] is not a valid property of class [" + entity.getName() 
+ "]");
         }
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/BasicCollectionInQuerySpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/BasicCollectionInQuerySpec.groovy
index b5ff16b8b8..73502404e5 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/BasicCollectionInQuerySpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/BasicCollectionInQuerySpec.groovy
@@ -82,7 +82,7 @@ class BasicCollectionInQuerySpec extends Specification {
         when:
         def results = BcStudent.createCriteria().list {
             createAlias("schools", "s")
-            'in'("s.elements", ["SchoolB"])
+            'in'("s", ["SchoolB"])
             projections {
                 property 'email'
             }
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 a58fe62514..ffea0a300e 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
@@ -1099,6 +1099,18 @@ class HibernateQuerySpec extends 
HibernateGormDatastoreSpec {
         preEvents > 0
         postEvents > 0
     }
+
+    def "test add and get aliases"() {
+        given:
+        def alias = new 
org.grails.orm.hibernate.query.HibernateAlias("nicknames", "n")
+
+        when:
+        hibernateQuery.addAlias(alias)
+
+        then:
+        hibernateQuery.getAliases().size() == 1
+        hibernateQuery.getAliases()[0] == alias
+    }
 }
 
 
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
index 660f300d1a..e0710946af 100644
--- 
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
@@ -139,6 +139,27 @@ class JpaCriteriaQueryCreatorSpec extends 
HibernateGormDatastoreSpec {
         then:
         noExceptionThrown()
     }
+
+    def "test createQuery with HibernateAlias triggers join"() {
+        given:
+        var entity = getPersistentEntity(JpaCriteriaQueryCreatorSpecPerson)
+        var detachedCriteria = new 
DetachedCriteria(JpaCriteriaQueryCreatorSpecPerson)
+        
+        // Mock HibernateQuery to provide an alias for a basic collection
+        def hibernateQuery = 
Mock(org.grails.orm.hibernate.query.HibernateQuery) {
+            getAliases() >> [new 
org.grails.orm.hibernate.query.HibernateAlias("nicknames", "n")]
+            getEntity() >> entity
+        }
+
+        var creator = new JpaCriteriaQueryCreator(new Query.ProjectionList(), 
criteriaBuilder, entity, detachedCriteria, new DefaultConversionService(), 
hibernateQuery)
+
+        when:
+        JpaCriteriaQuery<?> query = creator.createQuery()
+
+        then:
+        noExceptionThrown()
+        query != null
+    }
 }
 
 @Entity
@@ -146,6 +167,8 @@ class JpaCriteriaQueryCreatorSpecPerson implements 
GormEntity<JpaCriteriaQueryCr
     Long id
     String firstName
     String lastName
+    Set<String> nicknames
+    static hasMany = [nicknames: String]
 }
 
 @Entity

Reply via email to