You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ja...@apache.org on 2026/04/02 17:54:53 UTC
(grails-core) branch fix/h7-multi-datasource-executequery created (now 8a035be487)
This is an automated email from the ASF dual-hosted git repository.
jamesfredley pushed a change to branch fix/h7-multi-datasource-executequery
in repository https://gitbox.apache.org/repos/asf/grails-core.git
at 8a035be487 fix: normalize Groovy GString to String in Hibernate 7 criteria queries
This branch includes the following new commits:
new 8a035be487 fix: normalize Groovy GString to String in Hibernate 7 criteria queries
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(grails-core) 01/01: fix: normalize Groovy GString to String in Hibernate 7 criteria queries
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jamesfredley pushed a commit to branch fix/h7-multi-datasource-executequery
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 8a035be487249bda510eced81853b83c93b51133
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Thu Apr 2 13:54:39 2026 -0400
fix: normalize Groovy GString to String in Hibernate 7 criteria queries
Hibernate 7's SqmCriteriaNodeBuilder.valueParameter() throws
HibernateException when encountering Groovy's GStringImpl, unlike
Hibernate 5 which silently coerced it. Add normalizeValue() to
PredicateGenerator that converts CharSequence (including GString) to
String before passing values to JPA Criteria predicates.
Applied to Equals, NotEquals, IdEquals, SizeEquals, SizeNotEquals, and
Between handlers. Like/ILike already called toString() and were
unaffected. Removes @PendingFeature from MultipleDataSourceConnectionsSpec
which now passes all 4 tests.
Assisted-by: OpenCode <op...@opencode.ai>
---
.../orm/hibernate/query/PredicateGenerator.java | 25 ++++++++++++++++------
.../MultipleDataSourceConnectionsSpec.groovy | 2 --
2 files changed, 19 insertions(+), 8 deletions(-)
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 07fc841883..19e5be9f08 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
@@ -219,11 +219,11 @@ public class PredicateGenerator {
} else if (pc instanceof Query.Like c) {
return cb.like((Expression<String>) fullyQualifiedPath, c.getValue().toString());
} else if (pc instanceof Query.Equals c) {
- return cb.equal(fullyQualifiedPath, c.getValue());
+ return cb.equal(fullyQualifiedPath, normalizeValue(c.getValue()));
} else if (pc instanceof Query.NotEquals c) {
- return cb.or(cb.notEqual(fullyQualifiedPath, c.getValue()), cb.isNull(fullyQualifiedPath));
+ return cb.or(cb.notEqual(fullyQualifiedPath, normalizeValue(c.getValue())), cb.isNull(fullyQualifiedPath));
} else if (pc instanceof Query.IdEquals c) {
- return cb.equal(root.get("id"), c.getValue());
+ return cb.equal(root.get("id"), normalizeValue(c.getValue()));
} else if (pc instanceof Query.GreaterThan c) {
return cb.gt((Expression<? extends Number>) fullyQualifiedPath, getNumericValue(c));
} else if (pc instanceof Query.GreaterThanEquals c) {
@@ -233,9 +233,9 @@ public class PredicateGenerator {
} else if (pc instanceof Query.LessThanEquals c) {
return cb.le((Expression<? extends Number>) fullyQualifiedPath, getNumericValue(c));
} else if (pc instanceof Query.SizeEquals c) {
- return cb.equal(cb.size((Expression) fullyQualifiedPath), c.getValue());
+ return cb.equal(cb.size((Expression) fullyQualifiedPath), normalizeValue(c.getValue()));
} else if (pc instanceof Query.SizeNotEquals c) {
- return cb.notEqual(cb.size((Expression) fullyQualifiedPath), c.getValue());
+ return cb.notEqual(cb.size((Expression) fullyQualifiedPath), normalizeValue(c.getValue()));
} else if (pc instanceof Query.SizeGreaterThan c) {
return cb.gt(cb.size((Expression) fullyQualifiedPath), getNumericValue(c));
} else if (pc instanceof Query.SizeGreaterThanEquals c) {
@@ -245,7 +245,7 @@ public class PredicateGenerator {
} else if (pc instanceof Query.SizeLessThanEquals c) {
return cb.le(cb.size((Expression) fullyQualifiedPath), getNumericValue(c));
} else if (pc instanceof Query.Between c) {
- return cb.between((Expression) fullyQualifiedPath, (Comparable) c.getFrom(), (Comparable) c.getTo());
+ return cb.between((Expression) fullyQualifiedPath, (Comparable) normalizeValue(c.getFrom()), (Comparable) normalizeValue(c.getTo()));
}
return null;
}
@@ -541,6 +541,19 @@ public class PredicateGenerator {
((Query.NotIn) criterion).getSubquery();
}
+ /**
+ * Normalizes a criterion value for use with JPA Criteria API.
+ * Hibernate 7's SqmCriteriaNodeBuilder requires strict Java types and cannot
+ * coerce Groovy types like GString. This method converts CharSequence (including
+ * GString) to String so Hibernate can process the value correctly.
+ */
+ private static Object normalizeValue(Object value) {
+ if (value instanceof CharSequence && !(value instanceof String)) {
+ return value.toString();
+ }
+ return value;
+ }
+
private Number getNumericValue(Query.PropertyCriterion criterion) {
Object value = criterion.getValue();
if (value != null) {
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/connections/MultipleDataSourceConnectionsSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/connections/MultipleDataSourceConnectionsSpec.groovy
index ff0b5b77ab..f592f41ce5 100644
--- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/connections/MultipleDataSourceConnectionsSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/connections/MultipleDataSourceConnectionsSpec.groovy
@@ -26,7 +26,6 @@ import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.dialect.H2Dialect
import spock.lang.AutoCleanup
-import spock.lang.PendingFeature
import spock.lang.Shared
import spock.lang.Specification
@@ -119,7 +118,6 @@ class MultipleDataSourceConnectionsSpec extends Specification {
}
}
- @PendingFeature(reason = 'executeQuery with named parameters fails on multi-datasource entity in Hibernate 7 - QueryParameterException')
void "static GORM operations use first non-default datasource for multi datasource entity"() {
given: "a unique book name"
def uniqueName = "The Stand ${UUID.randomUUID()}"