Transactions

This section documents Hibernate ORM 7.4.x (User Guide, Introduction, Query Language Guide, Data Repositories Guide), Jakarta Persistence 3.2, Hibernate Search 8.4.x, and the Hibernate Validator / Hibernate Reactive references — which are the references these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against those official docs before being relied on in production.

Three older reference books were consulted as bibliography only while preparing these pages and are not the primary or main source for any page. All three predate Jakarta Persistence 3.2 and Hibernate ORM 6/7 (the javax.persistencejakarta.persistence namespace change, the ORM 6 query-engine rewrite, the Hibernate Search 6+ Elasticsearch backend), so the official documentation above wins on any discrepancy.

This section’s bibliography lists the reference material consulted while preparing these pages.

Hibernate never commits or rolls back a database change on its own — every write is scoped to an explicit transaction. This page covers the two transaction models Hibernate supports natively and how a session’s lifetime is usually scoped to one.

Resource-local vs. JTA

  • Resource-local — the simplest model: one EntityManager/Session talks to exactly one DataSource/JDBC connection, and the transaction is demarcated directly against it (EntityTransaction/Hibernate’s Transaction). No transaction manager or application server involvement. persistence.xml’s `transaction-type="RESOURCE_LOCAL" selects this.

  • JTA — a container-managed, potentially distributed transaction spanning multiple resources (two databases, a database and a JMS queue) coordinated by a jakarta.transaction.UserTransaction/ TransactionManager. Hibernate participates by registering a synchronization with the JTA transaction rather than owning commit/rollback itself. transaction-type="JTA" selects this; it needs an application server or a standalone JTA provider (Narayana, Atomikos, Bitronix).

Spring Boot applications almost always use resource-local transactions under the hood (via DataSourceTransactionManager/JpaTransactionManager) even though the application code only ever sees @Transactional — see Spring Data JPA's @Transactional/propagation section for how Spring’s declarative model maps onto this.

The Hibernate transaction API

Resource-local, using the JPA EntityTransaction:

EntityManager em = emf.createEntityManager();
try {
    em.getTransaction().begin();

    Book book = em.find(Book.class, id);
    book.setTitle("New title");

    em.getTransaction().commit();
} catch (RuntimeException e) {
    if (em.getTransaction().isActive()) {
        em.getTransaction().rollback();
    }
    throw e;
} finally {
    em.close();
}

The native Hibernate API (session.beginTransaction() / Transaction.commit() / Transaction.rollback()) is functionally equivalent for resource-local use and additionally exposes Transaction.registerSynchronization(…​) for hooking into the commit/rollback lifecycle without a full JTA setup.

Session-per-request and contextual sessions

The recommended session-scoping pattern — one Session/EntityManager per logical unit of work, typically one HTTP request or one message-processing invocation, opened at the start and closed at the end — rather than one long-lived session for the whole application or one per entity. A long-lived session accumulates managed entities indefinitely (unbounded first-level-cache growth, stale snapshots) and turns every stray lazy-load into a LazyInitializationException risk once the caller assumes it is still attached.

Hibernate can also expose a contextual current session (sessionFactory.getCurrentSession()) whose lifetime and scope is delegated to a pluggable CurrentSessionContext strategy configured via hibernate.current_session_context_class — thread (one session per thread, the default outside a container), jta (bound to the active JTA transaction), or a container-specific implementation. Frameworks (Spring) usually supply their own strategy rather than using this mechanism directly, since they already own request/transaction scoping.

Rollback and the persistence context

A transaction rollback does not automatically revert in-memory entity state to match the database — Hibernate leaves the persistence context as-is after a rollback, now representing state that was never committed. JPA’s own rule of thumb applies: after a rollback, the EntityManager should be treated as unusable and discarded (a new one obtained) rather than reused, precisely because its in-memory entities no longer reliably reflect the database. EntityManager.clear() mitigates this for entity state, but the persistence context’s own internal bookkeeping is not guaranteed consistent afterward — discarding it is the robust choice, and is exactly what session-per-request scoping already does naturally.