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 b033331bdf7a66e9fd42b9293d7a4e8febddec13
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sat Jun 28 01:44:32 2025 -0400

    Exists and Not Exists Predicates
---
 .../orm/hibernate/query/PredicateGenerator.java    | 57 ++++++++++++++----
 .../specs/hibernatequery/HibernateQuerySpec.groovy | 68 +++++-----------------
 2 files changed, 60 insertions(+), 65 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
index e8aadd5628..f1c09f5272 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/PredicateGenerator.java
@@ -11,12 +11,15 @@ import jakarta.persistence.criteria.Predicate;
 import jakarta.persistence.criteria.Root;
 import jakarta.persistence.criteria.Subquery;
 import org.grails.datastore.gorm.query.criteria.DetachedAssociationCriteria;
+import org.grails.datastore.mapping.model.PersistentEntity;
+import org.grails.datastore.mapping.model.types.Association;
 import org.grails.datastore.mapping.query.Query;
 import org.grails.datastore.mapping.query.api.QueryableCriteria;
 import org.hibernate.query.criteria.HibernateCriteriaBuilder;
 import org.hibernate.query.criteria.JpaInPredicate;
 import org.hibernate.query.criteria.JpaJoin;
 import org.hibernate.query.criteria.JpaPath;
+import org.hibernate.query.criteria.JpaPredicate;
 import org.hibernate.query.sqm.tree.domain.SqmPath;
 import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
 import org.hibernate.query.sqm.tree.predicate.SqmInListPredicate;
@@ -28,12 +31,14 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.time.ZonedDateTime;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 
 @Slf4j
@@ -182,21 +187,43 @@ public class PredicateGenerator {
                             return 
cb.not(cb.in(fromsByProvider.getFullyQualifiedPath(c.getProperty()), 
c.getValue()));
                         }
                     } else if (criterion instanceof Query.Exists c) {
-                        Subquery subquery = 
criteriaQuery.subquery(Object.class);
-                        Root subRoot = 
subquery.from(c.getSubquery().getPersistentEntity().getJavaClass());
+                        Subquery subquery = 
criteriaQuery.subquery(Integer.class);
+                        PersistentEntity childPersistentEntity = 
c.getSubquery().getPersistentEntity();
+                        Root subRoot = 
subquery.from(childPersistentEntity.getJavaClass());
+
+
                         JpaFromProvider newMap = (JpaFromProvider) 
fromsByProvider.clone();
                         newMap.put("root", subRoot);
-                        Predicate[] predicates = getPredicates(cb, 
criteriaQuery, subRoot, c.getSubquery().getCriteria(), newMap);
-                        
subquery.select(cb.literal(1)).where(cb.and(predicates));
-                        return cb.exists(subquery);
+                        var predicates = getPredicates(cb, criteriaQuery, 
subRoot, c.getSubquery().getCriteria(), newMap);
+
+                        var existsPredicate = getExistsPredicate(cb, root_, 
childPersistentEntity, subRoot);
+                        Predicate[] allPredicates = Stream.concat(
+                                Arrays.stream(predicates),
+                                Stream.of(existsPredicate)
+                        ).toArray(Predicate[]::new);
+
+                        
subquery.select(cb.literal(1)).where(cb.and(allPredicates));
+                        JpaPredicate exists = cb.exists(subquery);
+                        return exists;
                     } else if (criterion instanceof Query.NotExists c) {
-                        Subquery subquery = 
criteriaQuery.subquery(Object.class);
-                        Root subRoot = 
subquery.from(c.getSubquery().getPersistentEntity().getJavaClass());
+                        Subquery subquery = 
criteriaQuery.subquery(Integer.class);
+                        PersistentEntity childPersistentEntity = 
c.getSubquery().getPersistentEntity();
+                        Root subRoot = 
subquery.from(childPersistentEntity.getJavaClass());
+
+
                         JpaFromProvider newMap = (JpaFromProvider) 
fromsByProvider.clone();
                         newMap.put("root", subRoot);
-                        Predicate[] predicates = getPredicates(cb, 
criteriaQuery, subRoot, c.getSubquery().getCriteria(), fromsByProvider);
-                        
subquery.select(cb.literal(1)).where(cb.and(predicates));
-                        return cb.not(cb.exists(subquery));
+                        var predicates = getPredicates(cb, criteriaQuery, 
subRoot, c.getSubquery().getCriteria(), newMap);
+
+                        var existsPredicate = getExistsPredicate(cb, root_, 
childPersistentEntity, subRoot);
+                        Predicate[] allPredicates = Stream.concat(
+                                Arrays.stream(predicates),
+                                Stream.of(existsPredicate)
+                        ).toArray(Predicate[]::new);
+
+                        
subquery.select(cb.literal(1)).where(cb.and(allPredicates));
+                        JpaPredicate exists = cb.exists(subquery);
+                        return cb.not(exists);
                     } else if (criterion instanceof Query.SubqueryCriterion c) 
{
                         Subquery subquery = 
criteriaQuery.subquery(Number.class);
                         Root from = 
subquery.from(c.getValue().getPersistentEntity().getJavaClass());
@@ -257,6 +284,16 @@ public class PredicateGenerator {
         return list.toArray(new Predicate[0]);
     }
 
+    private static Predicate getExistsPredicate(HibernateCriteriaBuilder cb, 
From root_, PersistentEntity childPersistentEntity, Root subRoot) {
+        Association owner = childPersistentEntity
+                .getAssociations()
+                .stream()
+                .filter(assoc -> 
assoc.getAssociatedEntity().getJavaClass().equals(root_.getJavaType()))
+                .findFirst().orElseThrow();
+        Predicate existsPredicate = cb.equal(subRoot.get(owner.getName()), 
root_);
+        return existsPredicate;
+    }
+
     @SuppressWarnings("rawtypes")
     private static JpaInPredicate findInPredicate(HibernateCriteriaBuilder cb, 
Object projection, Path path, String subProperty) {
         return projection instanceof Query.PropertyProjection ? cb.in(path) : 
cb.in(((SqmPath) path).get(subProperty));
diff --git 
a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy
 
b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy
index b7b008e635..614daf4c0a 100644
--- 
a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy
+++ 
b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/hibernatequery/HibernateQuerySpec.groovy
@@ -2,12 +2,17 @@ package grails.gorm.specs.hibernatequery
 
 import grails.gorm.DetachedCriteria
 import grails.gorm.specs.HibernateGormDatastoreSpec
+import jakarta.persistence.criteria.CriteriaQuery
 import jakarta.persistence.criteria.JoinType
+import jakarta.persistence.criteria.Path
+import jakarta.persistence.criteria.Root
+import jakarta.persistence.criteria.Subquery
 import org.apache.grails.data.testing.tck.domains.*
 import org.grails.datastore.mapping.query.Query
 import org.grails.orm.hibernate.AbstractHibernateSession
 import org.grails.orm.hibernate.HibernateDatastore
 import org.grails.orm.hibernate.query.HibernateQuery
+import org.hibernate.query.criteria.JpaPredicate
 import spock.lang.Ignore
 
 
@@ -327,37 +332,14 @@ class HibernateQuerySpec extends 
HibernateGormDatastoreSpec {
         oldBob == newBob
     }
 
-//    @Ignore("Exits subquery is broken")
-    /**
-     * org.grails.orm.hibernate.query.PredicateGenerator.getPredicates()
-     * else if (criterion instanceof Query.Exists c)
-     select
-     p1_0.id,
-     p1_0.age,
-     p1_0.face_id,
-     p1_0.first_name,
-     p1_0.last_name,
-     p1_0.my_boolean_property,
-     p1_0.version
-     from
-     person p1_0
-     where
-     exists(select
-     1
-     from
-     pet p2_0
-     where
-     p2_0.owner_id=?)
-     offset
-     ? rows
-     */
     def exists() {
         given:
         new Person(firstName: "Fred", lastName: "Rogers", age: 52).save(flush: 
true)
         new Pet(name: "Lucky", owner: oldBob).save(flush:true)
         hibernateQuery.exists(
-                new DetachedCriteria(Pet).property("name").eq("owner", oldBob)
+                new DetachedCriteria(Pet)
         )
+
         when:
         def list = hibernateQuery.list()
         then:
@@ -365,40 +347,16 @@ class HibernateQuerySpec extends 
HibernateGormDatastoreSpec {
         oldBob == list.get(0)
     }
 
-    /**
-     *  org.grails.orm.hibernate.query.PredicateGenerator.getPredicates()
-     * else if (criterion instanceof Query.NotExists c)
-     select
-     p1_0.id,
-     p1_0.age,
-     p1_0.face_id,
-     p1_0.first_name,
-     p1_0.last_name,
-     p1_0.my_boolean_property,
-     p1_0.version
-     from
-     person p1_0
-     where
-     not exists(select
-     1
-     from
-     pet p2_0
-     where
-     p2_0.owner_id=?)
-     offset
-     ? rows
-     */
+
     def notExists() {
         given:
-        def fred = new Person(firstName: "Fred", lastName: "Rogers", age: 
52).save(flush: true)
-        def oldPet = new Pet(name: "Lucky")
-        oldBob.addToPets(oldPet)
-        oldBob.save(flush: true)
-        hibernateQuery.notExits(new DetachedCriteria(Pet).eq("owner", fred))
+        def newBob = new Person(firstName: "Fred", lastName: "Rogers", age: 
52).save(flush: true)
+        new Pet(name: "Lucky", owner: newBob).save(flush:true)
+        hibernateQuery.notExits(new DetachedCriteria(Pet))
         when:
-        def newBob = hibernateQuery.singleResult()
+        def result = hibernateQuery.singleResult()
         then:
-        oldBob == newBob
+        oldBob == result
     }
 
     def greaterThanAll() {

Reply via email to