Skip to content

[WIP] Require transactions by default for all Hibernate ORM session use#50019

Draft
yrodiere wants to merge 3 commits intoquarkusio:mainfrom
yrodiere:mandatory-transaction-for-hibernate
Draft

[WIP] Require transactions by default for all Hibernate ORM session use#50019
yrodiere wants to merge 3 commits intoquarkusio:mainfrom
yrodiere:mandatory-transaction-for-hibernate

Conversation

@yrodiere
Copy link
Copy Markdown
Member

@yrodiere yrodiere commented Sep 11, 2025

Please do not merge, I expect many tests will fail but I want to get a sense of how many.

We will need an entry in the migration guide.

cc @Sanne @gavinking

See also https://hibernate.zulipchat.com/#narrow/channel/132094-hibernate-orm-dev/topic/StatelessSession.20insertMultiple.20.2F.20updateMultiple.20.2F.20etc.2E/near/538862234

By the way, here is the relevant code:

} else if (requestScopedSessionEnabled) {
if (Arc.container().requestContext().isActive()) {
RequestScopedSessionHolder requestScopedSessions = this.requestScopedSessions.get();
return new SessionResult(requestScopedSessions.getOrCreateSession(unitName, sessionFactory),
false, false);
} else {
throw new ContextNotActiveException(
"Cannot use the EntityManager/Session because neither a transaction nor a CDI request context is active."
+ " Consider adding @Transactional to your method to automatically activate a transaction,"
+ " or @ActivateRequestContext if you have valid reasons not to use transactions.");
}
} else {
throw new ContextNotActiveException(
"Cannot use the EntityManager/Session because no transaction is active."
+ " Consider adding @Transactional to your method to automatically activate a transaction,"
+ " or set '" + HibernateOrmRuntimeConfig.extensionPropertyKey("request-scoped.enabled")
+ "' to 'true' if you have valid reasons not to use transactions.");
}

@quarkus-bot
Copy link
Copy Markdown

quarkus-bot Bot commented Sep 11, 2025

Thanks for your pull request!

Your pull request does not follow our editorial rules. Could you have a look?

  • title should preferably start with an uppercase character (if it makes sense!)

This message is automatically generated by a bot.

@quarkus-bot quarkus-bot Bot added the area/hibernate-orm Hibernate ORM label Sep 11, 2025
@quarkus-bot
Copy link
Copy Markdown

quarkus-bot Bot commented Sep 11, 2025

/cc @gsmet (hibernate-orm)

@Sanne
Copy link
Copy Markdown
Member

Sanne commented Sep 11, 2025

Great idea - tagging @FroMage for awareness

@quarkus-bot

This comment has been minimized.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Sep 11, 2025

😭 Deploy PR Preview failed.

@quarkus-bot

This comment has been minimized.

@FroMage
Copy link
Copy Markdown
Member

FroMage commented Sep 12, 2025

This sounds like we should have a @WithLazyTransaction on REST endpoints, to alleviate this issue. Kinda like HR has implicit @WithLazySession.

BTW, shouldn't we also require transactions for HR, then? Just to be consistent.

@Sanne
Copy link
Copy Markdown
Member

Sanne commented Sep 14, 2025

BTW, shouldn't we also require transactions for HR, then? Just to be consistent.

Perhaps yes, but bear in mind in that case the mechanism is quite different as it wouldn't be JTA. We'd need to think of the consequences of that - I'd be especially more concerned on starting them automatically (w/o an explicit opt-in) as we'd not want to give a false sense of safety w/o the user understanding the implications.

@yrodiere
Copy link
Copy Markdown
Member Author

This sounds like we should have a @WithLazyTransaction on REST endpoints, to alleviate this issue. Kinda like HR has implicit @WithLazySession.

Not sure why... ? Transactions are started lazily, no? Meaning on first connection request, which with Hibernate would happen on first load or flush?
@Transactional is not meant to start a transaction, just to delimit the transaction boundaries.

In any case, I strongly oppose adding many different annotations without thinking of a unified approach. The current state of things is quite confusing already.

@FroMage
Copy link
Copy Markdown
Member

FroMage commented Sep 15, 2025

Ah, so @Transactional is lazy already? I guess I forgot that.

So, hold on. In ORM, the session is lazy, and the TX is lazy. They're only opened when used by API.

Except, before this patch, we used to be able to access ORM for Read-Only operations.

Now, this means we can never use ORM from outside of an endpoint annotated with @Transactional.

This is becoming a 2-factor API: you can only use it with an annotation and using its API.

I do think we are going to have to talk about simplifying access to the ORM API, even though this can be done later, but perhaps REST endpoints by default should have an automatic @Transactional, assuming this works for both ORM and HR. In the future.

@yrodiere
Copy link
Copy Markdown
Member Author

This is becoming a 2-factor API: you can only use it with an annotation and using its API.

Technically you can also start a transaction programmatically with QuarkusTransaction. But yes, essentially, it is 2-factor: you need to specify the scope of whatever session you're using, and that requires specifying the transaction boundaries.

I do think we are going to have to talk about simplifying access to the ORM API, even though this can be done later, but perhaps REST endpoints by default should have an automatic @Transactional, assuming this works for both ORM and HR. In the future.

That would cause a lot of backwards incompatibility since it would effectively extend the scope of @Transactional methods called by REST endpoints.

We could instead have some sort of "last-minute" default transaction scoping that, assuming a request scope exists and no transaction scope exists when first accessing the Session, would start a transaction scope automatically and make sure it ends (commit/rollback) when the request scope ends.

But:

  1. It would work for Hibernate, but not for direct use of JDBC or Reactive SQL clients -- since we allow no-transaction access there.
  2. It feels very, very magic at this point. So much so that I shudder to think of the experience of a user trying to debug problems related to this behavior.
  3. Alternatively if you want to keep things simple you can just put @Transactional on your REST resources, at the class level. And then you're all set. It's not that complex.

@FroMage
Copy link
Copy Markdown
Member

FroMage commented Sep 15, 2025

I do think we are going to have to talk about simplifying access to the ORM API, even though this can be done later, but perhaps REST endpoints by default should have an automatic @Transactional, assuming this works for both ORM and HR. In the future.

That would cause a lot of backwards incompatibility since it would effectively extend the scope of @Transactional methods called by REST endpoints.

Yeah, because @Transactional defaults to REQUIRED instead of REQUIRES_NEW. True.

Well, the current PR is also backwards incompatible 🤷

  1. It would work for Hibernate, but not for direct use of JDBC or Reactive SQL clients -- since we allow no-transaction access there.

They use @Transactional? they don't use Hibernate Session though. So I'm not sure they're in scope for this change.

  1. It feels very, very magic at this point. So much so that I shudder to think of the experience of a user trying to debug problems related to this behavior.

This is essentially the default for HR and REST, no? If you don't specify a transaction/session, one will be provided for you at the request scope. Seems fairly straightforward and helpful to me.

  1. Alternatively if you want to keep things simple you can just put @Transactional on your REST resources, at the class level. And then you're all set. It's not that complex.

Except you need to do it on every REST resource, and it requires the user to first get an exception before acting on it. This is boilerplate. You're thinking about this with your expert hat, not your newbie hat. Which is good, it's useful, but you need both hats on :)

Anyway, I think this is mostly off-topic for this particular PR, sorry to have diverged on the topic.

@cescoffier
Copy link
Copy Markdown
Member

@yrodiere What's the status on this one?

@yrodiere yrodiere marked this pull request as draft January 5, 2026 07:56
@yrodiere
Copy link
Copy Markdown
Member Author

yrodiere commented Jan 5, 2026

@yrodiere What's the status on this one?

Many tests failing, as you can see.

@Sanne suggested we'd better be damn sure use all use cases work fine with transactions before we do this, which I agree with. There are still edge cases where not using a transaction is... arguably correct.

So, we'll need to address a good chunk of #51243 before we flip the switch here.

@yrodiere yrodiere force-pushed the mandatory-transaction-for-hibernate branch from 08ec626 to 8554216 Compare May 5, 2026 12:46
yrodiere added 3 commits May 6, 2026 11:36
…rios

Split CDI-based persistence unit tests into RequestScopeEnabled and RequestScopeDisabled
variants to explicitly test both behaviors:
- RequestScopeEnabled: reads work without transaction, writes require transaction
- RequestScopeDisabled: both reads and writes require transaction

This refactoring prepares for the upcoming change that will make request-scoped disabled
by default. The RequestScopeEnabled variants currently assume the default (enabled), while
RequestScopeDisabled variants explicitly disable it via config.

Deleted original test classes (now replaced by RequestScopeEnabled variants):
- MultiplePersistenceUnitsCdiEntityManagerTest
- MultiplePersistenceUnitsCdiSessionTest
- SinglePersistenceUnitCdiEntityManagerTest
- SinglePersistenceUnitCdiSessionTest

Deleted redundant tests (covered by new variants):
- SessionWithinRequestScopeTest/Disabled (uses same @ActivateRequestContext pattern)
- StatelessSessionWithinRequestScopeTest/Disabled (same pattern)
- NoTransactionTest (covered by NoRequestNoTransaction tests)

Assisted-By: Claude Sonnet 4.5 <[email protected]>
@yrodiere yrodiere force-pushed the mandatory-transaction-for-hibernate branch from 8554216 to 3979a20 Compare May 6, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/hibernate-orm Hibernate ORM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Require transactions by default for all Hibernate ORM session use

4 participants