What does the Hibernate configuration property hibernate.connection.isolation do, and what are the pitfalls of setting it when connections come from a pool or from a JTA datasource?
answer
- Pass-through to Connection.setTransactionIsolation
- Constant name or java.sql int
- Global to the session factory, never per transaction
- Pool must reset it, or it leaks to other borrowers
- Under JTA configure the datasource instead
basics
~20 sIt makes Hibernate call Connection.setTransactionIsolation on connections it obtains, using a java.sql.Connection constant or its symbolic name. It is global to the session factory, not per transaction, and it is the wrong place to set isolation for pooled or JTA-managed connections.
solid answer
~60 sThe property tells Hibernate to set the JDBC isolation level on each connection it acquires. The value is one of the java.sql.Connection TRANSACTION_ constants, either as the integer or, in current versions, as a readable name such as REPEATABLE_READ. Three limitations decide when to use it. It applies to the whole session factory: JPA has no standard API for per-transaction isolation, so you cannot raise it for one unit of work through this setting. It costs a round trip per connection acquisition on drivers that implement setTransactionIsolation as a statement. And on a pooled datasource it mutates borrowed connections — safe only if the pool resets the level on return or every borrower is Hibernate with the same setting. Under JTA it is worse: the transaction manager and datasource own connection setup and enlistment, so isolation belongs in the datasource configuration. The better default is to configure the level on the pool or datasource, and when one unit of work genuinely needs a different level, issue the vendor statement on that connection explicitly.
code
java · 13 lines// persistence.xml / properties
// hibernate.connection.isolation = REPEATABLE_READ (or the java.sql int, e.g. 4)
// one transaction at a different level: vendor statement, first thing in the tx
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
session.doWork(conn -> {
try (Statement s = conn.createStatement()) {
s.execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
}
});
// ... work ...
em.getTransaction().commit();go deeper
Know that it sets the JDBC isolation level for Hibernate's connections and that it applies to the whole application.
Explain that it is a pass-through to Connection.setTransactionIsolation, is factory-wide, and interacts badly with pools that do not reset connection state.
Prefer datasource or pool configuration, describe the per-acquisition round trip, and know the escape hatch for a single unit of work plus the retry burden a stronger level implies.
Treat isolation as a platform-level default set once at the datasource, with deviations isolated to dedicated units of work and justified in writing, because a global raise imposes retry semantics on every transaction in the system.
## What it maps to JDBC exposes isolation on the Connection: setTransactionIsolation(int) with TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ and TRANSACTION_SERIALIZABLE. hibernate.connection.isolation is a thin pass-through: when Hibernate obtains a connection it applies the configured value. Older configurations used the raw integers (2 for READ_COMMITTED, 4 for REPEATABLE_READ, 8 for SERIALIZABLE); current Hibernate also accepts the constant names, which is what you should write because the integers are easy to misread. If the property is unset, Hibernate leaves the connection at whatever level the driver or pool established, which in turn defaults to the server setting. ## Limitation 1: it is global The setting belongs to the session factory. Every transaction gets the same level. That is a poor fit for the usual requirement, which is to raise the level for a handful of sensitive operations and leave everything else at the default. JPA offers no standard per-transaction isolation API — LockModeType exists for row-level intent, but nothing sets a transaction's isolation. So options for one-off elevation are: a second persistence unit and datasource configured differently, or executing the vendor's SET TRANSACTION ISOLATION LEVEL statement on the connection before any work in that transaction, through a connection-unwrapping callback. Raising isolation globally is a large decision. Every transaction pays the cost, and on engines that detect conflicts, every transaction becomes a candidate for serialization failure — which means retry handling must exist everywhere, not just where you thought you needed stronger guarantees. ## Limitation 2: pooled connections A pool hands out connections and takes them back. If Hibernate mutates the isolation of a borrowed connection, the level persists on that physical connection unless the pool restores it on return. Modern pools generally do track and reset connection state, but relying on it silently couples correctness to pool behaviour and version. When several components share one pool — Hibernate, a plain JDBC helper, a migration tool — a mutated connection can hand a different isolation level to code that never asked for one. There is also a cost: on several drivers setTransactionIsolation is not a client-side flag but a statement sent to the server. Applying it on every acquisition adds a round trip to every transaction that touches the database. Configuring the level once on the pool, so it is applied when the physical connection is created, avoids paying it repeatedly. ## Limitation 3: JTA Under a JTA transaction manager, connections come from an enlisting datasource and their lifecycle includes suspend and resume. Aggressive connection release means Hibernate may acquire and release connections around statements rather than holding one for the transaction, so per-acquisition mutation becomes both wasteful and unreliable. Isolation is part of the datasource configuration in that model, and the Hibernate property is best left unset. ## What to do instead - Set the level where the connection is created: the pool or datasource configuration, or the database's default for that user or database. - Keep the global default at the engine's normal level, and solve specific anomalies with targeted mechanisms rather than a blanket raise. - When one unit of work truly needs a stronger level, isolate it: a dedicated datasource and persistence unit, or an explicit vendor statement executed as the first thing in that transaction, with a comment explaining why. - Verify empirically. Ask the database what the current transaction's isolation actually is rather than trusting configuration, because pool defaults, server defaults and framework settings all fight over the same knob and the last writer wins. ## When the property is fine A standalone application that owns its Hibernate-managed connections, has no other consumers of the same pool, is not under JTA, and wants one non-default level everywhere can set it and move on. That is a narrower situation than the property's prominence suggests, which is why interviewers use it to probe whether a candidate understands where connection state really lives.
- How would you raise the isolation level for one unit of work only?Not through this property, since it is factory-wide. Either route that work through a second persistence unit whose datasource is configured at the stronger level, or execute the vendor's SET TRANSACTION ISOLATION LEVEL statement on the connection as the very first action of the transaction, before any other statement. Then make sure that unit of work has retry handling, because stronger levels surface serialization failures.
- Why is configuring isolation on the connection pool usually better?The pool applies it when the physical connection is created and owns resetting it, so the level is consistent for every borrower and is not re-applied on each checkout. That avoids a per-transaction round trip on drivers where setTransactionIsolation is a server statement, and it removes the risk of a mutated connection being handed to code that expected the default.
saying these in an interview costs you the question
- Believing the property can vary isolation per transaction
- Assuming JPA has a standard per-transaction isolation API
- Setting it while under JTA and expecting the transaction manager to honour it
- Ignoring that a pooled connection may keep the mutated level after return
- Confusing LockModeType with transaction isolation