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 5bb1a644aab0e1454b4a7f829a2b7528feb7554b
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Thu Feb 19 13:49:41 2026 -0600

    Update HibernateQuerySpec for parity with HibernateCriteriaBuilderSpec and 
improve PredicateGenerator
---
 .../grails/orm/HibernateCriteriaBuilder.java       | 92 +++++++++++++---------
 .../orm/hibernate/query/PredicateGenerator.java    | 22 +-----
 .../specs/hibernatequery/HibernateQuerySpec.groovy | 84 +++++++++++++++++++-
 .../grails/orm/HibernateCriteriaBuilderSpec.groovy | 83 ++++++++++++++++++-
 4 files changed, 221 insertions(+), 60 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 c6d8ed3df7..3a3b6b14c1 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
@@ -108,42 +108,6 @@ public class HibernateCriteriaBuilder extends 
GroovyObjectSupport implements Bui
      * Define constants which may be used inside of criteria queries
      * to refer to standard Hibernate Type instances.
      */
-    public static final BasicTypeReference<Boolean> BOOLEAN = 
StandardBasicTypes.BOOLEAN;
-    public static final BasicTypeReference<Boolean> YES_NO = 
StandardBasicTypes.YES_NO;
-    public static final BasicTypeReference<Byte> BYTE = 
StandardBasicTypes.BYTE;
-    public static final BasicTypeReference<Character> CHARACTER = 
StandardBasicTypes.CHARACTER;
-    public static final BasicTypeReference<Short> SHORT = 
StandardBasicTypes.SHORT;
-    public static final BasicTypeReference<Integer> INTEGER = 
StandardBasicTypes.INTEGER;
-    public static final BasicTypeReference<Long> LONG = 
StandardBasicTypes.LONG;
-    public static final BasicTypeReference<Float> FLOAT = 
StandardBasicTypes.FLOAT;
-    public static final BasicTypeReference<Double> DOUBLE = 
StandardBasicTypes.DOUBLE;
-    public static final BasicTypeReference<BigDecimal> BIG_DECIMAL = 
StandardBasicTypes.BIG_DECIMAL;
-    public static final BasicTypeReference<BigInteger> BIG_INTEGER = 
StandardBasicTypes.BIG_INTEGER;
-    public static final BasicTypeReference<String> STRING = 
StandardBasicTypes.STRING;
-    public static final BasicTypeReference<Boolean> NUMERIC_BOOLEAN = 
StandardBasicTypes.NUMERIC_BOOLEAN;
-    public static final BasicTypeReference<Boolean> TRUE_FALSE = 
StandardBasicTypes.TRUE_FALSE;
-    public static final BasicTypeReference<java.net.URL> URL = 
StandardBasicTypes.URL;
-    public static final BasicTypeReference<Date> TIME = 
StandardBasicTypes.TIME;
-    public static final BasicTypeReference<Date> DATE = 
StandardBasicTypes.DATE;
-    public static final BasicTypeReference<Date> TIMESTAMP = 
StandardBasicTypes.TIMESTAMP;
-    public static final BasicTypeReference<Calendar> CALENDAR = 
StandardBasicTypes.CALENDAR;
-    public static final BasicTypeReference<Calendar> CALENDAR_DATE = 
StandardBasicTypes.CALENDAR_DATE;
-    public static final BasicTypeReference<Class> CLASS = 
StandardBasicTypes.CLASS;
-    public static final BasicTypeReference<Locale> LOCALE = 
StandardBasicTypes.LOCALE;
-    public static final BasicTypeReference<Currency> CURRENCY = 
StandardBasicTypes.CURRENCY;
-    public static final BasicTypeReference<TimeZone> TIMEZONE = 
StandardBasicTypes.TIMEZONE;
-    public static final BasicTypeReference<UUID> UUID_BINARY = 
StandardBasicTypes.UUID_BINARY;
-    public static final BasicTypeReference<UUID> UUID_CHAR = 
StandardBasicTypes.UUID_CHAR;
-    public static final BasicTypeReference<byte[]> BINARY = 
StandardBasicTypes.BINARY;
-    public static final BasicTypeReference<byte[]> IMAGE = 
StandardBasicTypes.IMAGE;
-    public static final BasicTypeReference<Blob> BLOB = 
StandardBasicTypes.BLOB;
-    public static final BasicTypeReference<byte[]> MATERIALIZED_BLOB = 
StandardBasicTypes.MATERIALIZED_BLOB;
-    public static final BasicTypeReference<char[]> CHAR_ARRAY = 
StandardBasicTypes.CHAR_ARRAY;
-    public static final BasicTypeReference<Character[]> CHARACTER_ARRAY = 
StandardBasicTypes.CHARACTER_ARRAY;
-    public static final BasicTypeReference<String> TEXT = 
StandardBasicTypes.TEXT;
-    public static final BasicTypeReference<Clob> CLOB = 
StandardBasicTypes.CLOB;
-    public static final BasicTypeReference<String> MATERIALIZED_CLOB = 
StandardBasicTypes.MATERIALIZED_CLOB;
-    public static final BasicTypeReference<Serializable> SERIALIZABLE = 
StandardBasicTypes.SERIALIZABLE;
 
     public static final String AND = "and"; // builder
     public static final String IS_NULL = "isNull"; // builder
@@ -1379,6 +1343,62 @@ public class HibernateCriteriaBuilder extends 
GroovyObjectSupport implements Bui
                 else {
                     hibernateQuery.isNotEmpty(propertyName);
                 }
+                return name;
+            }
+        }
+        else if (args.length >= 2 && args[0] instanceof String propertyName) {
+            propertyName = calculatePropertyName(propertyName);
+            switch (name) {
+                case RLIKE:
+                    return rlike(propertyName, args[1]);
+                case BETWEEN:
+                    if (args.length >= 3) {
+                        return between(propertyName, args[1], args[2]);
+                    }
+                    break;
+                case EQUALS:
+                    if (args.length == 3 && args[2] instanceof Map) {
+                        return eq(propertyName, args[1], (Map) args[2]);
+                    }
+                    return eq(propertyName, args[1]);
+                case EQUALS_PROPERTY:
+                    return eqProperty(propertyName, args[1].toString());
+                case GREATER_THAN:
+                    return gt(propertyName, args[1]);
+                case GREATER_THAN_PROPERTY:
+                    return gtProperty(propertyName, args[1].toString());
+                case GREATER_THAN_OR_EQUAL:
+                    return ge(propertyName, args[1]);
+                case GREATER_THAN_OR_EQUAL_PROPERTY:
+                    return geProperty(propertyName, args[1].toString());
+                case ILIKE:
+                    return ilike(propertyName, args[1]);
+                case IN:
+                    if (args[1] instanceof Collection) {
+                        return in(propertyName, (Collection) args[1]);
+                    } else if (args[1] instanceof Object[]) {
+                        return in(propertyName, (Object[]) args[1]);
+                    }
+                    break;
+                case LESS_THAN:
+                    return lt(propertyName, args[1]);
+                case LESS_THAN_PROPERTY:
+                    return ltProperty(propertyName, args[1].toString());
+                case LESS_THAN_OR_EQUAL:
+                    return le(propertyName, args[1]);
+                case LESS_THAN_OR_EQUAL_PROPERTY:
+                    return leProperty(propertyName, args[1].toString());
+                case LIKE:
+                    return like(propertyName, args[1]);
+                case NOT_EQUAL:
+                    return ne(propertyName, args[1]);
+                case NOT_EQUAL_PROPERTY:
+                    return neProperty(propertyName, args[1].toString());
+                case SIZE_EQUALS:
+                    if (args[1] instanceof Number) {
+                        return sizeEq(propertyName, ((Number) 
args[1]).intValue());
+                    }
+                    break;
             }
         }
         throw new MissingMethodException(name, getClass(), args);
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 12ff7965ff..08de63641b 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
@@ -108,27 +108,7 @@ public class PredicateGenerator {
                         } else if (criterion instanceof 
Query.SizeLessThanEquals c) {
                             return cb.le(cb.size(fullyQualifiedPath), 
getNumericValue(c));
                         } else if (criterion instanceof Query.Between c) {
-                            if (c.getFrom() instanceof String && c.getTo() 
instanceof String) {
-                                return cb.between(fullyQualifiedPath, (String) 
c.getFrom(), (String) c.getTo());
-                            } else if (c.getFrom() instanceof Short && 
c.getTo() instanceof Short) {
-                                return cb.between(fullyQualifiedPath, (Short) 
c.getFrom(), (Short) c.getTo());
-                            } else if (c.getFrom() instanceof Integer && 
c.getTo() instanceof Integer) {
-                                return cb.between(fullyQualifiedPath, 
(Integer) c.getFrom(), (Integer) c.getTo());
-                            } else if (c.getFrom() instanceof Long && 
c.getTo() instanceof Long) {
-                                return cb.between(fullyQualifiedPath, (Long) 
c.getFrom(), (Long) c.getTo());
-                            } else if (c.getFrom() instanceof Date && 
c.getTo() instanceof Date) {
-                                return cb.between(fullyQualifiedPath, (Date) 
c.getFrom(), (Date) c.getTo());
-                            } else if (c.getFrom() instanceof Instant && 
c.getTo() instanceof Instant) {
-                                return cb.between(fullyQualifiedPath, 
(Instant) c.getFrom(), (Instant) c.getTo());
-                            } else if (c.getFrom() instanceof LocalDate && 
c.getTo() instanceof LocalDate) {
-                                return cb.between(fullyQualifiedPath, 
(LocalDate) c.getFrom(), (LocalDate) c.getTo());
-                            } else if (c.getFrom() instanceof LocalDateTime && 
c.getTo() instanceof LocalDateTime) {
-                                return cb.between(fullyQualifiedPath, 
(LocalDateTime) c.getFrom(), (LocalDateTime) c.getTo());
-                            } else if (c.getFrom() instanceof OffsetDateTime 
&& c.getTo() instanceof OffsetDateTime) {
-                                return cb.between(fullyQualifiedPath, 
(OffsetDateTime) c.getFrom(), (OffsetDateTime) c.getTo());
-                            } else if (c.getFrom() instanceof ZonedDateTime && 
c.getTo() instanceof ZonedDateTime) {
-                                return cb.between(fullyQualifiedPath, 
(ZonedDateTime) c.getFrom(), (ZonedDateTime) c.getTo());
-                            }
+                            return cb.between(fullyQualifiedPath, (Comparable) 
c.getFrom(), (Comparable) c.getTo());
                         } else if (criterion instanceof Query.ILike c) {
                             return cb.ilike(fullyQualifiedPath, 
c.getValue().toString());
                         } else if (criterion instanceof Query.RLike c) {
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 b4b7059c35..d21c8580ec 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
@@ -40,7 +40,7 @@ class HibernateQuerySpec extends HibernateGormDatastoreSpec {
     }
 
     def setupSpec() {
-        manager.addAllDomainClasses([Person, Pet, Face,EagerOwner])
+        manager.addAllDomainClasses([Person, Pet, Face, EagerOwner, 
CommonTypes, BigDecimalEntity])
     }
 
     def equals() {
@@ -524,6 +524,46 @@ class HibernateQuerySpec extends 
HibernateGormDatastoreSpec {
         oldBob == newBob
     }
 
+    def betweenBigDecimal() {
+        given:
+        HibernateDatastore hibernateDatastore = manager.hibernateDatastore
+        AbstractHibernateSession session = hibernateDatastore.connect() as 
AbstractHibernateSession
+        HibernateQuery query = new HibernateQuery(session, 
hibernateDatastore.getMappingContext().getPersistentEntity(BigDecimalEntity.typeName))
+        new BigDecimalEntity(amount: 10.5G).save(flush: true, failOnError: 
true)
+        new BigDecimalEntity(amount: 20.5G).save(flush: true, failOnError: 
true)
+        new BigDecimalEntity(amount: 30.5G).save(flush: true, failOnError: 
true)
+
+        query.between("amount", 15.0G, 25.0G)
+
+        when:
+        def results = query.list()
+
+        then:
+        results.size() == 1
+        results[0].amount == 20.5G
+    }
+
+    def inListArray() {
+        new Person(firstName: "Fred", lastName: "Rogers", age: 52).save(flush: 
true)
+        given:
+        hibernateQuery.in("age", [50, 52])
+        when:
+        def results = hibernateQuery.list()
+        then:
+        results.size() == 2
+        results*.firstName.sort() == ["Bob", "Fred"]
+    }
+
+    def countDistinct() {
+        new Person(firstName: "Bob", lastName: "The Builder", age: 
25).save(flush: true)
+        given:
+        hibernateQuery.projections().countDistinct("firstName")
+        when:
+        def count = hibernateQuery.singleResult()
+        then:
+        count == 1 // Both are "Bob"
+    }
+
     def joinWithProjection() {
         given:
         oldBob.addToPets(new Pet(name:"Lucky")).save(flush:true)
@@ -657,6 +697,40 @@ class HibernateQuerySpec extends 
HibernateGormDatastoreSpec {
         age == 51
     }
 
+    def sumBigDecimal() {
+        given:
+        HibernateDatastore hibernateDatastore = manager.hibernateDatastore
+        AbstractHibernateSession session = hibernateDatastore.connect() as 
AbstractHibernateSession
+        HibernateQuery query = new HibernateQuery(session, 
hibernateDatastore.getMappingContext().getPersistentEntity(BigDecimalEntity.typeName))
+        new BigDecimalEntity(amount: 100.0G).save(flush: true, failOnError: 
true)
+        new BigDecimalEntity(amount: 200.0G).save(flush: true, failOnError: 
true)
+
+        query.projections().sum("amount")
+
+        when:
+        def sum = query.singleResult()
+
+        then:
+        sum == 300.0G
+    }
+
+    def avgBigDecimal() {
+        given:
+        HibernateDatastore hibernateDatastore = manager.hibernateDatastore
+        AbstractHibernateSession session = hibernateDatastore.connect() as 
AbstractHibernateSession
+        HibernateQuery query = new HibernateQuery(session, 
hibernateDatastore.getMappingContext().getPersistentEntity(BigDecimalEntity.typeName))
+        new BigDecimalEntity(amount: 100.0G).save(flush: true, failOnError: 
true)
+        new BigDecimalEntity(amount: 200.0G).save(flush: true, failOnError: 
true)
+
+        query.projections().avg("amount")
+
+        when:
+        def avg = query.singleResult()
+
+        then:
+        avg == 150.0G
+    }
+
     def groupByLastNameAverageAge() {
         def fred = new Person(firstName: "Fred", lastName: "Rogers", age: 52)
         fred.save(flush: true)
@@ -739,3 +813,11 @@ class HibernateQuerySpec extends 
HibernateGormDatastoreSpec {
 }
 
 
+
[email protected]
+class BigDecimalEntity implements Serializable {
+    Long id
+    Long version
+    BigDecimal amount
+}
+
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
index d8ea1cf885..3e63b316fb 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
@@ -80,6 +80,53 @@ class HibernateCriteriaBuilderSpec extends 
HibernateGormDatastoreSpec {
         results[0].firstName == "Fred"
     }
 
+    void "test rlike criteria"() {
+        when:
+        def results = builder.list {
+            rlike("firstName", "^F.*")
+        }
+
+        then:
+        results.size() == 1
+        results[0].firstName == "Fred"
+    }
+
+    void "test between criteria"() {
+        when:
+        def results = builder.list {
+            between("balance", BigDecimal.valueOf(100), 
BigDecimal.valueOf(300))
+        }
+
+        then:
+        results.size() == 2
+        results*.firstName.sort() == ["Fred", "Wilma"]
+    }
+
+    void "test sizeEq criteria"() {
+        when:
+        def results = builder.list {
+            sizeEq("transactions", 2)
+        }
+
+        then:
+        results.size() == 1
+        results[0].firstName == "Fred"
+    }
+
+    void "test isEmpty and isNotEmpty criteria"() {
+        when:
+        def emptyResults = builder.list {
+            isEmpty("transactions")
+        }
+        def notEmptyResults = builder.list {
+            isNotEmpty("transactions")
+        }
+
+        then:
+        emptyResults.size() == 3 // Wilma, Pebbles, Bam-Bam
+        notEmptyResults.size() == 2 // Fred, Barney
+    }
+
     void "test isNull criteria"() {
         when:
         def results = builder.list {
@@ -168,11 +215,43 @@ class HibernateCriteriaBuilderSpec extends 
HibernateGormDatastoreSpec {
         when:
         def results = builder.list {
             geProperty("balance", "balance") // always true, validates path
+            eqProperty("firstName", "firstName")
+            neProperty("firstName", "lastName")
+            gtProperty("balance", "balance") // always false for same property
             order("balance", "desc")
         }
         then:
-        results[0].firstName == "Pebbles"
-        results[-1].firstName == "Bam-Bam"
+        results.size() == 0 // because gtProperty("balance", "balance") is 
false
+
+        when:
+        results = builder.list {
+            leProperty("balance", "balance")
+            ltProperty("balance", "balance")
+        }
+        then:
+        results.size() == 0
+    }
+
+    void "test nested criteria with aliases"() {
+        when:
+        def results = builder.list {
+            transactions {
+                eq("amount", BigDecimal.valueOf(50))
+            }
+        }
+        then:
+        results.size() == 1
+        results[0].firstName == "Barney"
+
+        when:
+        results = builder.list {
+            transactions {
+                between("amount", BigDecimal.valueOf(15), 
BigDecimal.valueOf(25))
+            }
+        }
+        then:
+        results.size() == 1
+        results[0].firstName == "Fred"
     }
 
     void "test projections countDistinct groupProperty min max"() {

Reply via email to