jdaugherty opened a new pull request, #16479:
URL: https://github.com/apache/grails-core/pull/16479

   ## Problem
   
   On Hibernate 7, an equality or ordering comparison on a property mapped with 
a custom `UserType` fails in dynamic finders, criteria and where queries:
   
   ```
   org.hibernate.query.SemanticException: Cannot compare left expression of 
type 'com.example.Money'
   with right expression of type 'java.io.Serializable'
   ```
   
   ```groovy
   class Item {
       Money amount
       static mapping = { amount type: MoneyUserType }
   }
   
   Item.findAllByAmountGreaterThan(money)                // fails
   Item.createCriteria().list { eq 'amount', money }     // fails
   Item.where { amount <= money }.list()                 // fails
   Item.executeQuery('from Item i where i.amount > :a', [a: money])  // works
   ```
   
   `eq`, `ne`, `gt`, `ge`, `lt` and `le` are affected. `inList` and `between` 
are not.
   
   ## Cause
   
   `PredicateGenerator.convertComparisonValue` wrapped every comparison value 
in `criteriaBuilder.literal(value)`. Hibernate types an untyped literal from 
the value's class. A class it doesn't know, if it implements `Serializable`, 
gets the generic `Serializable` type, and `TypecheckUtil.assertComparable` then 
rejects comparing it with the `UserType` path. HQL works because a query 
parameter is typed from the path it is compared with. Before edbe4f4879 these 
comparisons used the JPA overloads that take a value, and those infer the type 
the same way.
   
   ## Fix
   
   - Plain values are no longer wrapped in a literal. `equal`/`notEqual` (which 
already take an `Object`) and 
`greaterThan`/`greaterThanOrEqualTo`/`lessThan`/`lessThanOrEqualTo` get the 
value itself, so Hibernate infers its type from the property path, the same as 
for HQL parameters. This uses only the public criteria API.
   - Property arithmetic expressions (`gt('quantity', reorderLevel * 2)`) still 
go through the expression overloads.
   - A value that is not `Comparable` can't be passed to JPA's ordering 
overloads, so it still goes through a literal, as before. This keeps working 
for value classes that are not `Serializable`, which Hibernate leaves untyped.
   
   **Known remaining edge:** an ordering comparison (`gt`/`ge`/`lt`/`le`) on a 
value class that is `Serializable` but not `Comparable` still fails as before. 
`eq`/`ne` work for it. The public `HibernateCriteriaBuilder` has no factory for 
a value typed from a path. Closing this edge would mean calling 
`SqmCriteriaNodeBuilder#value(Object, SqmExpression)`, which is internal 
Hibernate API, so this PR leaves it out.
   
   ## Tests
   
   New `UserTypeComparisonSpec` (H7 core) with a `Serializable`, `Comparable` 
`UserType` value class covers:
   - all six operators through dynamic finders, criteria and where queries
   - a detached-criteria `count()`, plus `inList`/`between`
   - HQL as the control
   - a second `UserType` whose class is neither `Comparable` nor 
`Serializable`, covering the literal fallback for all six operators
   - a property-arithmetic comparison, covering the expression path
   
   Mutation-checked:
   - The spec against the old `PredicateGenerator`: the 19 `Serializable` 
user-type comparisons fail with the `SemanticException` above. The rest pass.
   - Dropping the non-`Comparable` fallback (always casting to `Comparable`) 
fails the 4 ordering comparisons on the second type.
   
   Run locally:
   - `UserTypeComparisonSpec`, `PredicateGeneratorSpec`
   - the H7 query/criteria specs plus the TCK criteria, finder, where, 
detached-criteria, enum, range, negation, association, null and common-types 
specs: 745 tests, 0 failures
   - `:grails-data-hibernate7-core:codeStyle`
   
   The full module suite was not run locally; CI covers it.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to