Expand Hibernate 7 functional coverage and query parity#15731
Expand Hibernate 7 functional coverage and query parity#15731jamesfredley wants to merge 44 commits into
Conversation
…5 and 7 Replace the separate functional and hibernate5Functional CI jobs with a single functional job that uses a hibernate-version matrix (['5', '7']). This ensures all 20+ general functional tests run against both Hibernate versions without duplicating test projects. Changes: - functional-test-config.gradle: Add -PhibernateVersion property that uses Gradle dependency substitution to redirect grails-data-hibernate5 to grails-data-hibernate7 for general (non-labeled) test projects. Excludes h5-only runtime deps (hibernate-ehcache, jboss-transaction-api) when testing with Hibernate 7. Add skipHibernate5Tests and skipHibernate7Tests properties to the onlyIf block. - gradle.yml: Merge functional and hibernate5Functional into one job with hibernate-version matrix. Each matrix slot skips the opposite version's labeled projects. Update publish job dependencies. Assisted-by: Claude Code <Claude@Claude.ai>
The h7 boot-plugin AutoConfiguration.imports was empty, preventing Spring Boot from discovering HibernateGormAutoConfiguration when grails-data-hibernate7 is on the classpath. This caused GORM has not been initialized correctly errors in functional tests running with Hibernate 7 via dependency substitution. Also remove Hibernate 5-specific EhCache region factory configuration from the gorm and hyphenated functional test application.yml files. EhCache is not available with Hibernate 7, and the second-level cache is not needed by these tests. Assisted-by: Claude Code <Claude@Claude.ai>
…sion=7 Five general functional test projects use Hibernate 5-specific GORM APIs that changed in Hibernate 7. Skip them when running with -PhibernateVersion=7 rather than letting them fail. Their h7-compatible equivalents already exist in grails-test-examples/hibernate7/. Incompatible projects: - app1: HibernateSpec unit test domain class detection differs in h7 - datasources: ChainedTransactionManager commit behavior changed in h7 - gorm: executeUpdate(String) requires Map parameter in h7 - views-functional-tests: depends on h5-specific caching config - scaffolding-fields: integration test context fails under h7 Assisted-by: Claude Code <Claude@Claude.ai>
- Fix 17 plain executeUpdate('...') calls across 7 specs in
grails-test-examples/gorm to use executeUpdate('...', [:]).
H7's HibernateGormStaticApi rejects plain CharSequence args
(requires either a GString with interpolated params or the
Map overload).
- Add getDomainClasses() override to BookHibernateSpec in app1.
H7's HibernateSpec uses HibernateDatastoreSpringInitializer
which requires explicit domain class declaration; H5 auto-detected
via classpath scanning.
- Remove grails-test-examples-app1 and grails-test-examples-gorm
from h7IncompatibleProjects list — both now run cleanly under
Hibernate 7 via dependency substitution.
Remaining excluded: datasources (ChainedTransactionManager),
views-functional-tests (HAL/JSON diffs), scaffolding-fields
(grails-fields rendering diffs).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…urn types, cross-property arithmetic Bug 2: HibernateQueryExecutor.singleResult() now catches both org.hibernate.NonUniqueResultException and jakarta.persistence.NonUniqueResultException (H7 throws the JPA variant; the original catch missed it) and returns the first result instead of propagating. Bug 4: HqlQueryContext.aggregateTargetClass() now returns precise types per function: count() → Long, avg() → Double, sum/min/max() → Number. Previously all aggregates were bound to Long, causing QueryTypeMismatchException in H7's strict SQM type checking. Bug 5: Cross-property arithmetic in where-DSL (e.g. pageCount > price * 10) was silently dropped — the RHS property reference was coerced to a literal. Fixed via: - PropertyReference: Groovy wrapper returned by propertyMissing for numeric properties; *, +, -, / operators produce a PropertyArithmetic value object - PropertyArithmetic: value type carrying (propertyName, Operator, operand) - HibernateDetachedCriteria: H7-only DetachedCriteria subclass that overrides propertyMissing to return PropertyReference for numeric properties, and newInstance() to preserve the subtype through cloning - HibernateGormStaticApi: overrides where/whereLazy/whereAny to use HibernateDetachedCriteria as the closure delegate - PredicateGenerator: resolveNumericExpression() detects PropertyArithmetic and builds cb.prod/sum/diff/quot(path, operand) instead of a literal H5 and MongoDB are unaffected — all new types are confined to grails-data-hibernate7. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ve on managed entities H7 enforces strict collection identity during flush. GORM's addTo* and save() flow had two failure modes: 1. When an entity is already managed in the current Hibernate session, calling session.merge() causes H7 to create a second PersistentCollection for the same role+key alongside the one already tracked in the session cache -> 'Found two representations of same collection'. Fix (HibernateGormInstanceApi.performMerge): check session.contains(target) before merging. If the entity is already managed, skip merge entirely; dirty-checking and cascade will handle children on flush. 2. When addTo* is called on a managed entity, GormEntity.addTo uses direct field access (reflector.getProperty) which bypasses H7's bytecode-enhanced interceptor, sees null, and creates a plain ArrayList on the field. H7's session cache already tracks a PersistentBag/Set for that role -> two representations on the next save. Fix (HibernateEntity.addTo): override addTo in the H7 trait; for managed entities (id != null), trigger the H7 interceptor via InvokerHelper.getProperty to obtain the live PersistentCollection before delegating to GormEntity.super.addTo. Fix (HibernateEntityTransformation): re-target the concrete addToXxx generated methods so their internal addTo call dispatches through HibernateEntity.addTo rather than being hard-wired to GormEntity.addTo. Fix (HibernateGormInstanceApi.reconcileCollections): detect stale PersistentCollections (session != current session) and replace them with plain collections before merge, covering any edge cases where the H7 interceptor path is not taken. Adds AddToManagedEntitySpec with 4 tests covering: - addTo on an already-persisted entity - multiple addTo on a fresh transient entity - modify child + save twice - removeFrom + save Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…and proper applicationClass - Replace executeQuery(plainString) and executeUpdate(plainString) calls with the (String, Map) overloads (empty map for parameterless queries). HibernateGormStaticApi intentionally rejects plain String in the no-arg overload to prevent HQL injection; parameterless static queries must use the Map overload. - Add applicationClass = Application to @Integration so the spec shares the same application context and transaction manager as the other specs in this module, preventing test-data bleed between specs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…checkstyle - Validate -PhibernateVersion accepts only '5' or '7' and fail the build fast with a GradleException for any other value (addresses Copilot review on line 31). - Rename the counter-intuitive isHibernate5/isHibernate7/isMongo booleans in functional-test-config.gradle. The previous names used a leading ! and matched projects that were NOT that version, making the onlyIf conditions confusing. Replace with positive isHibernate5Project / isHibernate7Project / isMongoTaskProject and collapse nested ifs (addresses Copilot and @sanjana2505006 feedback). - Fix SingleSpaceSeparator checkstyle violations in PredicateGenerator.java switch arms (blocking CI Core Projects check). Assisted-by: claude-code:claude-opus-4
…ate-matrix-testing # Conflicts: # .github/workflows/gradle.yml # gradle/functional-test-config.gradle # grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/HibernateDetachedCriteria.groovy
…tional tests
When -PhibernateVersion=7 is set, general (non-hibernate-labeled) functional test projects get the grails-data-hibernate5 module substituted with grails-data-hibernate7 via dependency substitution. However, those test projects also import platform(project(':grails-bom')) directly, which ships the Hibernate 5 version constraints.
Extend the dependency substitution to also swap the default grails-bom to grails-hibernate7-bom in the same projects, so the transitive graph gets a consistent set of Hibernate 7 version constraints. This matches the pattern used by the h7-labeled test projects in grails-test-examples/hibernate7/, which import grails-hibernate7-bom directly.
Without this, the H7 matrix slot fails with:
Could not resolve org.hibernate.orm:hibernate-core.
Cannot find a version of 'org.hibernate.orm:hibernate-core' that satisfies the version constraints
Assisted-by: claude-code:claude-opus-4
Two fixes to unblock CI after the recent BOM restructure on 8.0.x-hibernate7: 1) grails-forge-core/build.gradle was referencing the old grails-bom/build/publications/... path. The BOM was moved to grails-bom/default/ in the split, so grailsVersionInfo now fails with 'input file expected to be present but it does not exist'. Point the task at the new location. 2) grails-hibernate7-bom pinned hibernate.version = 7.2.5.Final via a strictly constraint, while spring-boot-dependencies:4.0.5 (transitively imported through grails-base-bom) pulls hibernate-core 7.2.7.Final. The conflicting constraints cause 'Cannot find a version of org.hibernate.orm:hibernate-core that satisfies the version constraints' whenever a general test project consumes the Hibernate 7 stack. Bump hibernate.version to 7.2.7.Final to match Spring Boot's managed version. Assisted-by: claude-code:claude-opus-4
…ge analytics
Two follow-up fixes after the previous commit's CI run still showed missing-version resolution failures:
1) gradle/functional-test-config.gradle: the previous module-level substitution of org.apache.grails:grails-bom -> grails-hibernate7-bom didn't catch direct project references. Test build files (e.g. grails-test-examples-app1) declare 'platform(project(':grails-bom'))' literally, so dependency-substitution by module name does not match. Add a project-level substitution 'project(':grails-bom') -> project(':grails-hibernate7-bom')' that fires for the same general-functional-tests + hibernateVersion=7 condition, ensuring transitive Hibernate 7 constraints (hibernate-models, jandex, hibernate-tools-orm) get versioned.
2) grails-forge/grails-forge-analytics-postgres/build.gradle: was missing platform('io.micronaut:micronaut-bom') across all dependency configurations, so 'org.testcontainers:testcontainers-postgresql' resolved with no version and broke the Forge build. Add the platform import to annotationProcessor / implementation / testAnnotationProcessor / testImplementation to mirror the pattern used by grails-forge-cli.
Assisted-by: claude-code:claude-opus-4
…ency buckets
The previous attempt to substitute project(':grails-bom') -> project(':grails-hibernate7-bom') did not propagate version constraints to consumers because grails-data-hibernate7-core declares 'implementation platform(project(':grails-hibernate7-bom'))', and 'implementation' is not visible to downstream consumers. As a result, general functional test projects still failed with 'Could not find org.hibernate.models:hibernate-models:.' and 'Could not find io.smallrye:jandex:.' when -PhibernateVersion=7.
Drop the project-level substitute (which had no effect) and instead attach the Hibernate 7 BOM directly to the test project's dependency buckets (implementation, compileOnly, runtimeOnly, testImplementation, etc). The H7 BOM brings the version constraints into the test project's runtimeClasspath / compileClasspath where the unversioned transitive dependencies need them.
Assisted-by: claude-code:claude-opus-4
Micronaut BOM 3.10.4 sets a testcontainers.version property but does not actually publish testcontainers entries in dependencyManagement, so 'org.testcontainers:testcontainers-postgresql' kept resolving with no version. Import the testcontainers BOM explicitly to give the test classpath a real version. Assisted-by: claude-code:claude-opus-4
…ostgresql The dependency 'org.testcontainers:testcontainers-postgresql' does not exist in Maven Central. The actual artifact published in the testcontainers BOM is 'org.testcontainers:postgresql'. The previous testcontainers BOM import was correct, but the consumer side still used the wrong artifact name. Assisted-by: claude-code:claude-opus-4
Same root cause as the previous commit for grails-forge-analytics-postgres: 'org.testcontainers:testcontainers-spock' is not a published artifact; the correct id is 'org.testcontainers:spock'. Add the testcontainers BOM so the version is managed centrally. Assisted-by: claude-code:claude-opus-4
Add testcontainersVersion=1.20.4 to grails-forge/gradle.properties and reference it via the property in both forge build.gradle files instead of the inline literal. Assisted-by: claude-code:claude-opus-4
Bring the branch up to date with its base (761 commits). Conflicts resolved to combine both approaches: - .github/workflows/gradle.yml: keep the base's dedicated hibernate5Functional/hibernate7Functional jobs and the static skip flags on the main job; the main functional job retains this branch's hibernate-version matrix so general functional tests run against both Hibernate 5 and 7. - gradle/functional-test-config.gradle: keep this branch's -PhibernateVersion BOM redirection and h7IncompatibleProjects, plus the base's doNotCacheTests caching and complete only/skip filter set; standardize on the base's isHibernate5/isHibernate7/isMongo naming. - grails-forge build files: use the base's centrally managed dependency versions. - grails-test-examples/hyphenated application.yml: keep both the base's ehcache region.factory_class and this branch's endpoints.jmx config. Assisted-by: claude-code:claude-opus-4-8
…o test/h7-functional-coverage
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Assisted-by: Hephaestus:openai/gpt-5.5
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 8.0.x-hibernate7 #15731 +/- ##
===========================================================
+ Coverage 19.0184% 49.0318% +30.0134%
- Complexity 288 16344 +16056
===========================================================
Files 57 1937 +1880
Lines 3423 91614 +88191
Branches 597 16004 +15407
===========================================================
+ Hits 651 44920 +44269
- Misses 2642 39703 +37061
- Partials 130 6991 +6861
🚀 New features to boost your workflow:
|
borinquenkid
left a comment
There was a problem hiding this comment.
Try to use HqlQueryContext it has all the HQL Logic
Route findWhere-generated HQL through shared preparation so query settings and hints are preserved while retaining property validation. Add mapped-column and read-only hint coverage, remove stale EhCache config from H7 sample, and fix H5/TCK style regressions from CI. Assisted-by: Hephaestus:openai/gpt-5.5 oracle
Restore safe same-instance attach semantics without replication, add regression coverage for stale and deleted attach cases, and align the H5/H7 compatibility fixes needed by the functional coverage lane. Assisted-by: opencode:gpt-5.5 oracle
Update the Hibernate 7 BOM constraints to use hibernate-models 1.1.1 so Spring Dependency Management can select Hibernate ORM 7.4.x without loading an incompatible hibernate-models API. Assisted-by: opencode:gpt-5.5
|
I am highly against arbitrary substitution of hibernate modules. We should be cloning apps to enhance the coverage. Otherwise we will be dealing with unexpected dependency resolutions |
|
@jdaugherty please explain why? We were missing a ton of issues and breaking changes due to the missing tests on h7? This is working and tests are not production code for end apps. This is DRY I think we should run the same tests against both. |
Assisted-by: opencode:openai/gpt-5.5
Assisted-by: opencode:openai/gpt-5.5
Assisted-by: opencode:openai/gpt-5.5
Assisted-by: opencode:openai/gpt-5.5
Assisted-by: opencode:openai/gpt-5.5
Assisted-by: opencode:openai/gpt-5.5
|
Updated the Hibernate 7 path to Hibernate ORM 7.4 because Spring Boot 4.1 has moved its managed Hibernate version there. Hibernate 7.3 is already in limited-support mode, so staying there would leave Grails targeting a line that is not expected to receive the ongoing updates we need. Hibernate 7.4 is the actively maintained 7.x line, so aligning with Boot 4.1 and 7.4 keeps the Hibernate 7 integration on the version that will continue to get fixes. |
Assisted-by: opencode:openai/gpt-5.5
|
Pushed a docs follow-up commit (a5cd019) and updated the PR description. The PR body now targets Hibernate ORM 7.4, expands the fixed-issue table for the 7.4 dependency/API, proxy, identifier, generator, and event-listener fixes, and marks resolved rows in the breaking-change checklist. The Hibernate 7 docs now document the remaining end-application migration items after the PR, including Hibernate 7.3/7.4 behavior and DDL changes that are not resolved by Grails API fixes. |
…perty hierarchy Extract sortOrIndexColumns, getReferencedColumns, and setSorted dispatch from CompositeIdentifierToManyToOneBinder into the appropriate abstraction layers: - GrailsHibernatePersistentEntity: add sortOrIndexForeignKeyColumns() and getReferencedIdentifierColumns() — identifier column knowledge belongs on the entity that owns the PersistentClass - HibernatePersistentProperty: add default markValueSorted(SimpleValue) that handles ToOne and DependantValue via value-type dispatch — kept in the default (rather than subtype overrides) because DependentKeyValueBinder passes a HibernateToManyProperty with a DependantValue key, making value-type dispatch the correct strategy - CompositeIdentifierToManyToOneBinder: remove four private helper methods; bindCompositeIdentifierToManyToOne now delegates in three lines - CompositeIdentifierToManyToOneBinderSpec: revert to original two PR tests
✅ All tests passed ✅🏷️ Commit: 3ee18b8 Learn more about TestLens at testlens.app. |
I previously have explained this on multiple PRs. The issue is it complicates the setup, running locally, and troubleshooting dependency resolution. Any missing coverage we should add tests for, you have already identified the gaps so it should be easy. |
borinquenkid
left a comment
There was a problem hiding this comment.
I made changes to improve the complexity and testability
| if (possibleProject.name == 'grails-bom') { | ||
| substitute module(substitutedArtifact) using platform(project(':grails-bom')) | ||
| def targetBom = redirectBomToH7 ? ':grails-hibernate7-bom' : ':grails-bom' | ||
| substitute module(substitutedArtifact) using platform(project(targetBom)) |
There was a problem hiding this comment.
@borinquenkid my issue is this file, specifically this substitution.
There was a problem hiding this comment.
"Hey @jdaugherty, that makes total sense. In an OSS context, hidden classpath manipulation like this easily rots if left unchecked, and I agree we don't want people troubleshooting brittle IDE syncs locally.
Here is compromise so we can hit our end-of-June release target for Hibernate 7 parity without abandoning your architectural goal:
Fail-Safe Logging: I will add an explicit, loud deprecation warning or a hardcoded TODO block directly above this substitution in functional-test-config.gradle tracking its removal.
Immediate Post-Release Issue: I'll open a tracking issue right now to completely untangle this layer by cloning the apps into a clean directory structure.
Given that I want to completely drop Hibernate 5 support in the near future anyway, duplicating the entire H5 test matrix right this second might end up being throwaway work. Can we use this substitution as a temporary bridge to unblock the June release gate, with the explicit agreement that the cleanup issue is the immediate next priority?"
There was a problem hiding this comment.
I'm strongly a -1 on this. People will just ignore the depreciation and it doesn't solve our actual problem. The majority of the work I've done with Grails is dependency debugging - probably over 50% of my time has been spent on it. This completely breaks that workflow. By substituting arbitrarily, we now have to populate that option throughout our workflows to be able to debug.
I think we have to switch hibernate modules for functional tests. I don't think it is scalable to duplicate all test apps. It will create a lot of duplication and a heavy maintenance burden. Can we create shell scripts or other solution to easily run both/all versions locally? |
We don't need to duplicate the functional apps themself, but rather the test coverage in the TCK or the example apps to ensure proper coverage. The problem with having switches for functionality is the build tools behave differently based on those switches. This means:
We effectively hide behavior behind a switch and Gradle is not designed for that. I'm a strong no for this reason. I believe we can add test coverage, but tests in a functional app for UrlMappings don't need hibernate and shouldn't need to be cloned. We should clone the tests that are relevant or add scenarios for them. We need to do this anyway to prevent regressions. I realize that substitution is a short cut to getting this, but I view it as a major blocker for regular development & proper project hygiene. |
Summary
This PR expands the Hibernate 7 functional-test path and closes several parity gaps found while running the functional applications against Hibernate 7. The branch targets
8.0.x-hibernate7and includes the functional matrix work plus follow-up fixes, documentation, Hibernate ORM 7.4 alignment, and review-gate cleanup.The main outcome is that Hibernate 5 and Hibernate 7 functional lanes can run from the same workflow while known H7-incompatible general projects remain explicitly excluded until their association-rendering, unique-constraint, and datasource gaps are fixed.
Spring Boot 4.1 now manages Hibernate ORM 7.4, and Hibernate ORM 7.4 is the current stable Hibernate 7 line. Hibernate ORM 7.3 and 7.2 are limited-support lines, so this PR updates the Hibernate 7 path to Hibernate ORM
7.4.1.Finalwhile keeping Hibernate Tools on7.3.8.Finalbecause no Hibernate Tools 7.4 artifact is published.Detailed issue coverage
-PhibernateVersion=7is selected.grails-hibernate7-bomfor eligible general functional tests and aligns the H7 Hibernate version to7.4.1.Final.7.4.1.Finaland documents that Hibernate 7.4 is the target line.org.hibernate.tool:hibernate-tools-orm:7.4.1.Finalorhibernate-tools-utils:7.4.1.Final.hibernate-tools.versionand pins Hibernate Tools to7.3.8.Finalwhile ORM uses7.4.1.Final.isViewargument toInFlightMetadataCollector.addTable(...).isView=false.Map<String, String>for settings that Hibernate now accepts asMap<String, Object>.grails-data-hibernate7-dbmigration-corecan fail Java compilation against Hibernate 7.4.Map<String, Object>.ByteBuddyInterceptorpath.SimpleIdBinderinstalled an emptyPrimaryKeybefore Hibernate created the table primary key.RootClass.createPrimaryKey()to build the populated primary key from the bound identifier columns.GrailsIdentityGeneratorSpecfails even though production generator behavior is valid.GeneratorCreationContext.getType().getReturnedClass()in the affected specs.EventSource.asEventSource()andgetFactory()in this path.HibernateEventListenerSpecfails during listener invocation.asEventSource()and the mocked session factory fromgetFactory().datasources,views-functional-tests, andscaffolding-fieldsare not safe to run as generic H7 substitution projects yet.h7IncompatibleProjectsand documents the reason for each exclusion directly ingradle/functional-test-config.gradle.io.micronaut.platform:micronaut-platformand centralizes the Testcontainers version used by the Forge H7 path.executeQueryor related calls fail with unsupported or unsafe String HQL usage.getSingleResult()returns more than one row.DetachedCriteria.get()can throwNonUniqueResultExceptionin H7 where H5 returned one result.Found two representations of same collection.avg,max,min, andsumdoes not return the domain entity type.pageCount > price * 10can fail with coercion errors.findWhereresult limitingfindWheredid not reliably limit duplicate matches to a single row.findWhereis a single-result convenience API and should not allow duplicate matches to produce multiple-row failures.findWhereto setmax: 1internally and adds SQL-capture coverage proving the generated query includes the row limit.findWhereandfindAllWhere= nulldoes not match null rows; it must beis null.findWhere(nullableProperty: null)andfindAllWhere(nullableProperty: null)fail to find rows with null values.is nullfor null values and removes those values from named parameters.findWhereandfindAllWherewere interpolated into HQL without validation.getAllordering with convertible idsgetAll(["3", "1", "2"])can return nulls or the wrong order even though those rows exist.fetchSize,timeout,readOnly,cache,max,offset,flushMode, andlockto behave consistently.maxoroffsetcan fail casts,cachemight not be applied, andlockwas not filtered from named parameters.lock: trueto pessimistic write locking, disables query cache for locked queries, and filterslockfrom query parameters.HibernateGormDatastoreSpecimports, removes duplicate setup, and narrows Spock imports.Something.groovyreferencedBookwithout the required imported package.Bookis ingrails.gorm.tests.hasmany, not the support class package.Bookimport.Department.getPackage().getAllordering, and remaining Hibernate 7.4 migration differences.lock,findWhere,findAllWhere,getAll, and the remaining Hibernate 7.4 migration checklist.H7_GORM_BUG_REPORT.mdwas useful triage context but lacked an Apache header and read like current expected failure status.Verification
git diff --check origin/8.0.x-hibernate7...HEADcompleted with no output.H7_GORM_BUG_REPORT.mdcompleted with no output.H7_GORM_BUG_REPORT.mdfound no diagnostics.HqlQueryMethods.javaandSelectHqlQuery.javafound no diagnostics../gradlew --no-daemon --no-parallel :grails-data-hibernate7-core:test --tests "org.grails.orm.hibernate.HibernateGormStaticApiFindWhereSpec" --tests "org.grails.orm.hibernate.HibernateGormStaticApiSpec" --tests "org.grails.orm.hibernate.query.HqlQueryMethodsSpec" --tests "org.grails.orm.hibernate.query.SelectHqlQuerySpec"passed after cleaning:grails-core: 130 tests, 130 successes, 0 failures../gradlew :grails-data-hibernate7-core:test --rerun-tasks --stacktracepassed: 2921 tests, 0 failures../gradlew :grails-data-hibernate7-dbmigration-core:compileJavapassed../gradlew :grails-data-hibernate7-dbmigration-core:test --rerun-tasks --stacktracepassed../gradlew aggregateViolations --continuepassed../gradlew :grails-data-hibernate7-docs:asciidoctorpassed after adding the Hibernate 7.4 migration checklist. Existing AsciiDoctor warnings remain in unrelated pre-existing docs../gradlew --no-daemon --no-parallel :grails-data-hibernate7-docs:asciidoctor :grails-doc:publishGuide -x aggregateGroovydocpassed and built the user manual before the final Hibernate 7.4 checklist expansion.Known follow-up work not included here
grails-test-examples-datasourcesgrails-test-examples/hibernate7/grails-multiple-datasources, but not all datasource-switching and OSIV coverage has an H7 duplicate.grails-test-examples-views-functional-testsh7IncompatibleProjects.grails-test-examples-scaffolding-fieldsHibernate 7.4 vs Hibernate 5.6 end-application breaking-change checklist
This table is the migration checklist for application code. It calls out the places where Hibernate 7.4 is stricter or behaves differently than Hibernate 5.6, what can break in real applications, whether this PR resolves the Grails-facing issue, and what application owners should change or verify.
grails-data-hibernate5, H5 BOM constraints, or H5-only transitive dependencies with the H7 runtime can fail dependency resolution or application boot.executeQueryandexecuteUpdateBook.executeQuery("from Book where inStock = true")can be rejected or fail in tests that previously passed.executeQuery("from Book where inStock = true", [:]), positional params, named params, or a GString path where parameters are deliberately bound.getSingleResult()strictness and throws when more than one row matches.NonUniqueResultExceptionwhere H5 returned one row.maxResults(1)or use a list query and take the first result intentionally.findWhereduplicate matchesfindWherebehaves as a first-match helper.findWhere(prop: null)andfindAllWhere(prop: null)as null checks.is null; passing null as a named value does not match null rows.= :paramwith a null value.is nulloris not nullexplicitly rather than= :paramwith a null parameter.findWhereorfindAllWherenow fails fast.getAllorder with converted idsgetAll(["3", "1", "2"])can return null entries or wrong ordering if id conversion is not considered.in (:ids)queries, remember SQL does not preserve input order unless you reorder results yourself.select avg(price)orselect max(pageCount)can fail if they are created as typed entity queries or mapped to the wrong return type.@Queryaggregate return types.avggenerally returnsDouble;maxandminfollow the selected expression type;countreturnsLong.where { pageCount > price * 10 }can fail when operands resolve to incompatible numeric types.addTo*, cascade saves, detached graph merges, or replacing collection instances can throwFound two representations of same collection.addTo*andremoveFrom*helpers where possible.lock: trueis used.max: "10",offset: "5",readOnly: "true",fetchSize: "50", ortimeout: "30"can fail if passed directly to strict Hibernate APIs.intandbooleanvalues.lock,max, oroffsetcan be mistaken for named HQL parameters if not filtered.QueryTimeoutExceptionorLockTimeoutExceptionfor query and lock timeout paths.PersistenceExceptionwhen the database marks the transaction for rollback.PersistenceExceptionaround direct Hibernate/JPA timeout-sensitive code and inspect the cause when necessary.java.sqltemporal values.java.timetemporal values by default.java.sql.Date,Time, orTimestamp.java.timetypes, or sethibernate.query.native.prefer_jdbc_datetime_types=trueduring migration.not nullby default.@Versioncolumnsnot nullby default.dbCreate=update, especially for legacy nullable version columns.char/Character, Oracle floating point,@ElementCollectionsets,@CreationTimestamp,@UpdateTimestamp, and Oracle 23c LOB columns.import.sqlimport.sqlon the classpath can run where it was previously ignored.import.sqlfiles from the runtime classpath or make schema-generation settings explicit.hibernate.max_fetch_depth=2default.hibernate.max_fetch_depthexplicitly if the application relies on that behavior.max/offsetor limits with collection fetch joins can return a different row/window shape.org.hibernate.limitInMemoryquery hint only when the previous in-memory behavior is required.current dateandlocal datecould preserve a time component because Oracle has no true date-only function.trunc(current_date).current dateorlocal dateand change the expression if a timestamp is required.@Anymappings@Anymappings were loaded by a separate select.@Anyassociations when loading an entity by id.@Anymappings can produce different SQL shape and row width.@Anymappings for performance and row duplication implications.hibernate-community-dialectsunderorg.hibernate.community.dialect.hibernate-coreunderorg.hibernate.dialect.org.hibernate.dialect.SpannerPostgreSQLDialector rely on automatic dialect resolution.NOT_AUDITEDassociationsNOT_AUDITEDwas requested.RelationTargetAuditMode.NOT_AUDITED.RelationTargetAuditMode.NOT_AUDITED.HibernateDatastoresetup can miss domain or service classes if the scanned package is wrong.