Skip to content

Draft: Establish JPA 4 compatibility baseline with minimal changes - #4296

Closed
oscarfanchin wants to merge 15 commits into
spring-projects:mainfrom
oscarfanchin:wip/jpa4
Closed

Draft: Establish JPA 4 compatibility baseline with minimal changes#4296
oscarfanchin wants to merge 15 commits into
spring-projects:mainfrom
oscarfanchin:wip/jpa4

Conversation

@oscarfanchin

@oscarfanchin oscarfanchin commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

This draft provides an exploratory compatibility baseline for Jakarta Persistence 4 and Hibernate ORM 8.

The goal is to compile and run the existing Spring Data JPA functionality against this new baseline, validate the migration direction, and identify follow-up work.

This is not a complete or final support statement.

Scope

  • Minimal compatibility adaptations for Jakarta Persistence 4 and Hibernate ORM 8.
  • Test adjustments required by API or provider behavior changes.
  • Existing Spring Data JPA functionality only.

Support for new Jakarta Persistence 4 features is intentionally out of scope.

Intentional exclusions

  • EclipseLink tests are excluded because no Jakarta Persistence 4-compatible EclipseLink build is currently available.
  • Querydsl tests are disabled because the Querydsl version currently used by the project is not compatible with Jakarta Persistence 4.

Dependency baseline

This branch currently uses snapshot dependencies where required because the relevant Hibernate ORM and Jakarta Persistence APIs are still evolving.

More stable milestone builds are available, but they do not yet provide the compatibility surface required by this draft.

Current status

  • The project compiles.
  • 3,591 tests were run: 3,413 passed, 108 were skipped, 7 failed, and 63 ended in errors.
  • All 70 failures and errors are confined to three AOT repository test suites.
  • All executed non-AOT tests pass, including the database-specific tests.
  • The Spring Data Envers module test suite passes without failures.
  • AOT named-query discovery requires dedicated follow-up work.

Test command:

mvn -pl spring-data-jpa -Pall-dbs clean process-test-classes
surefire:test@unit-test
surefire:test@integration-test
surefire:test@mysql-test
surefire:test@postgres-test
surefire:test@oracle-test
-Dmaven.test.failure.ignore=true -U

Full Surefire reports from the test run are attached.
surefire-reports.zip

AOT note

The remaining AOT failures originate from named-query discovery in QueriesFactory.

With the current Hibernate ORM 8 baseline, untyped named queries may be represented by a TypedQueryReference whose result type is null.

QueriesFactory currently probes multiple result-type candidates, including null, when looking up named queries. Passing the null candidate to EntityManagerFactory.getNamedQueries(Class<R>) results in an NPE.

Removing the null candidate avoids the NPE but does not solve the underlying problem: untyped named queries can no longer be discovered. This causes AOT repository method contributions to be omitted and produces cascading metadata and integration-test failures.

This behavior needs further discussion and dedicated follow-up work, potentially involving coordination with Hibernate ORM.

Related issues

Related to #4187
Related to #4197

  • You have read the Spring Data contribution guidelines.
  • You use the code formatters provided here and have them applied to your changes. Don’t submit any formatting related changes.
  • You submit test cases (unit or integration tests) that back your changes.
  • You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 6, 2026
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
Signed-off-by: Oscar Fanchin <oscar.fanchin@gmail.com>
@oscarfanchin

Copy link
Copy Markdown
Contributor Author

Just a small follow-up: I intend to keep this PR in draft until Hibernate ORM 8 reaches the RC stage, since the underlying APIs are still evolving.

The goal of this PR is not to provide a complete and final Jakarta Persistence 4 migration at this stage. It is deliberately a minimal adaptation of the current baseline, intended to provide a working starting point for the subsequent changes and to avoid discovering major integration issues only at the last minute.

I’m keeping the branch aligned with the latest JPA 4.0 and Hibernate ORM 8 changes and periodically testing the integration as the APIs evolve.

In the meantime, some initial triage on the overall migration direction would be very useful. I’m not requesting a full code review yet; I would mainly like to understand whether this baseline approach is aligned with the Spring Data JPA roadmap and whether the AOT named-query discovery issue described above should be addressed here or coordinated with Hibernate ORM.

@mp911de — since you opened #4187, some initial directional feedback on this baseline would be greatly appreciated.

Thank you for your attention and for all the work you do on the project.

@SuppressWarnings("unchecked")
public TypedQuery<Long> doCreateCountQuery(JpaParametersParameterAccessor accessor) {
return (TypedQuery<Long>) countQuery.createQuery(accessor);
return (TypedQuery<Long>) (countQuery.createQuery(accessor).unwrap(TypedQuery.class));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Adding a instanceof check would be helpful with a fallback to unwrap(…).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taken on board, addressed in #4307.

@mp911de

mp911de commented Jul 29, 2026

Copy link
Copy Markdown
Member

Thanks for having a look. The PR description is LLM-bloated and, especially the state on how many tests ran etc. 😉

A full move to a JPA 4.0 baseline requires a broader refactoring towards TypedQuery, as JPA's Query now carries several @Deprecated(since = "4.0", forRemoval = true) declarations.

I suggest keeping this change with a focus on #4197 to enable compatibility with Hibernate 7 and 8. Bridging the breaking changes between these versions requires some reflective glue. Consequently, our CI should run tests against both versions.

For now, the breaking changes appear to be limited to HibernateUtil, so we could introduce two reflective adapters for Hibernate 7 and 8, respectively.

@oscarfanchin

Copy link
Copy Markdown
Contributor Author

Thanks for having a look. The PR description is LLM-bloated and, especially the state on how many tests ran etc. 😉

A full move to a JPA 4.0 baseline requires a broader refactoring towards TypedQuery, as JPA's Query now carries several @Deprecated(since = "4.0", forRemoval = true) declarations.

I suggest keeping this change with a focus on #4197 to enable compatibility with Hibernate 7 and 8. Bridging the breaking changes between these versions requires some reflective glue. Consequently, our CI should run tests against both versions.

For now, the breaking changes appear to be limited to HibernateUtil, so we could introduce two reflective adapters for Hibernate 7 and 8, respectively.

hi @mp911de

First of all, thank you for the review and for taking the time to look into this.
I’d like to clarify which compatibility model you have in mind:

  1. The same codebase is compiled separately against each baseline: one build against JPA 3.2, tested with Hibernate 7.4.x and EclipseLink 5.0.x, and another build against JPA 4.0, currently tested only with Hibernate 8.0. Each build is expected to work only with the baseline and providers it was compiled against, using minimal reflective adapters to isolate the changed APIs.

  2. We continue compiling and releasing the artifact against the JPA 3.2 baseline, but the same binary is also expected to work at runtime with JPA 4.0 and Hibernate 8, using reflective adapters to bridge the differences.

Are you suggesting the first model, mainly to keep the codebase ready for the eventual baseline upgrade, or the second one, where the artifact compiled against JPA 3.2 must already support Hibernate 8 at runtime?
Before presenting this version of the PR, I already had something prepared for the first model. I have not yet explored the second scenario.

@mp911de

mp911de commented Jul 29, 2026

Copy link
Copy Markdown
Member

It is indeed the second variant. We do not intend to produce another artifact or version. Instead, the same Spring Data JPA version should support both Hibernate 7 with JPA 3.2 and Hibernate 8 with JPA 4.0.

For the November release, this approach leaves us with a JPA 3.2 baseline. Moving the baseline to JPA 4.0 and Hibernate 8.0, together with a compatible EclipseLink version, will require some more time until all involved parties are ready. There seems to be very little movement in EclipseLink who released their 5.0 GA version only just recently. I therefore assume EclipseLink being one of the later JPA 4.0 adopters leaving us with the possibility to raise the JPA baseline only at a later time, maybe even with a Spring Data 5.0 release. I am happy to get more perspectives for better consideration.

@oscarfanchin

oscarfanchin commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

hi @mp911de,
great, this is exactly what I expected would be needed and, honestly, what I was hoping for. It should be a very interesting challenge.
As soon as I have some free time, I will start from the other branch I already have locally. My plan is to keep the current profiles and add two more: one for the Hibernate 8.0 branch with JPA 4.0 M6, and one for Hibernate master, which has already moved to 8.1 and the latest JPA 4.0 snapshots. This should cover both Hibernate 8 lines while retaining the current Hibernate 7 setup. Does that sound like the right approach?
Given the change in scope and objective, I think it would be cleaner to open a separate PR.
The AOT issues I mentioned will probably remain, at least initially. Hibernate’s implementation of EntityManagerFactory.getNamedQueries(Class<?>) currently does not seem to return named queries without an explicitly declared result type when called with Object.class. Our current fallback then tries null, which results in an immediate NPE. This probably needs further investigation together with the Hibernate team.

@oscarfanchin

oscarfanchin commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Closing this one: superseded by #4307, which takes the single-artifact direction. Discussion continues there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants