In plain Hibernate, what is the difference between SessionFactory.openSession() and SessionFactory.getCurrentSession(), and what determines the scope and lifetime of a "current" session?
answer
- openSession = new + you close it
- getCurrentSession = bound + auto-closed at commit
- hibernate.current_session_context_class: thread | jta | managed
- no transaction -> 'could not obtain transaction-synchronized Session'
- Hibernate 6: inTransaction / fromTransaction
basics
~20 sopenSession() always creates a new session that you must close yourself. getCurrentSession() returns the session bound to the current context — thread or JTA transaction, per hibernate.current_session_context_class — creating it on first call and closing it automatically at transaction end.
solid answer
~50 s`openSession()` is unconditional: a brand-new `Session` with a fresh persistence context, and **you own its lifecycle**. Forgetting `close()` leaks a persistence context and, usually, a pooled JDBC connection. `getCurrentSession()` delegates to a `CurrentSessionContext` strategy chosen by `hibernate.current_session_context_class`: - **`thread`** — `ThreadLocalSessionContext` binds one session to the calling thread. The first call inside a transaction creates and binds it; on commit or rollback Hibernate flushes, closes and unbinds it. Repeat calls on that thread return the same session, which is how a service and a DAO share a unit of work without passing it around. - **`jta`** — the session is bound to the JTA transaction and closed when it completes. - **`managed`** — an external component binds and unbinds explicitly. So the trade is explicit ownership versus ambient ownership. `getCurrentSession` requires an active transaction and demands that the thread be a genuine unit-of-work boundary. Hibernate 6 largely supersedes both with `sessionFactory.inTransaction(session -> ...)`, which opens, commits and closes for you.
code
java · 10 lines// you own it
try (Session s = sessionFactory.openSession()) {
Transaction tx = s.beginTransaction();
s.persist(order);
tx.commit();
}
// bound to the thread; closed automatically at commit
Session s = sessionFactory.getCurrentSession();
s.persist(order); // same instance for every component in this transactiongo deeper
Know that openSession creates a session you must close, and getCurrentSession returns a contextual one you must not close.
Name the current_session_context_class strategies, explain the transaction-scoped bind/flush/close/unbind cycle, and why a transaction must be active.
Focus on failure modes: leaks from missing close, ThreadLocal bleed on pooled threads, async work crossing the boundary, and why lexically scoped helpers are the safer modern default.
Argue about where unit-of-work boundaries belong architecturally — ambient thread binding versus explicit scopes or passed context — and what each choice costs in testability and in async or reactive execution models.
## The problem being solved A persistence context should span exactly one unit of work, and every component in that unit of work should use the *same* one — otherwise two components loading the same row get two managed instances, dirty checking runs twice, and lazy references initialised in one component are unusable in the other. Passing the session down every call is explicit but noisy, so Hibernate offers an ambient lookup: `getCurrentSession()`. ## openSession ```java Session session = sessionFactory.openSession(); try { Transaction tx = session.beginTransaction(); ... tx.commit(); } finally { session.close(); } ``` Properties: always a new instance, never bound anywhere, never auto-closed, and it can be used without a transaction (reads only, in practice). Its correctness depends entirely on a `finally` block or try-with-resources — `Session` implements `AutoCloseable`. Missing `close()` is the number-one Hibernate resource leak: the persistence context stays reachable and the connection stays checked out of the pool, so under load the pool drains and the application appears to hang rather than fail. ## getCurrentSession and the context strategies `getCurrentSession()` asks a `CurrentSessionContext` implementation for 'the' session: **`thread` — ThreadLocalSessionContext.** A `ThreadLocal<Session>`. The first call on a thread opens a session, binds it, and registers a synchronization on the transaction. On commit the session is flushed and closed, then unbound; on rollback it is closed and unbound. Subsequent calls in the same thread and transaction return the identical instance, so calling code never closes it — doing so manually breaks the contract. **`jta` — JTASessionContext.** The session is bound to the current JTA transaction rather than the thread, which is the correct choice when a transaction can migrate between threads or when several resources are enlisted. **`managed` — ManagedSessionContext.** Hibernate does nothing on its own; an outer component calls `ManagedSessionContext.bind(session)` and `unbind(factory)`. This is what integration layers use when they want to control the boundary themselves — for instance opening the session at the start of a request and closing it at the end. You can also supply your own class name implementing `CurrentSessionContext`. ## Consequences worth stating in an interview 1. **A transaction is required** (for `thread`/`jta`). Calling `getCurrentSession()` with no transaction started throws `HibernateException: Could not obtain transaction-synchronized Session for current thread`. The session's lifetime is defined by the transaction, so there is nothing to bind it to otherwise. 2. **The thread is the boundary.** Handing work to an executor, a parallel stream, or an async callback moves it to a thread with a different (or no) bound session. Passing the session object into that thread is worse still — sessions are not thread-safe. Each task needs its own unit of work. 3. **Leaks are structural.** `ThreadLocal` binding on a pooled request thread means an unbound-but-not-closed session can outlive the request and be observed by the next task on that thread. Strategies that unbind on transaction completion avoid this; hand-rolled ThreadLocal caching of sessions does not. 4. **Flush on commit.** With the `thread` strategy, commit flushes and closes; you neither flush nor close manually. Code that calls `session.close()` on a current session breaks later components in the same transaction. ## Modern practice Hibernate 6 provides functional helpers that make the whole question mostly historical for new code: ```java sessionFactory.inTransaction(session -> { ... }); // void Order o = sessionFactory.fromTransaction(s -> s.find(Order.class, 1L)); ``` These open a session, begin a transaction, commit or roll back on exception, and close — the boundary is a lexical scope instead of an ambient one, which is easier to reason about and impossible to leak. When a managed environment already defines the unit of work, the `managed` context (or the container's own injection of an `EntityManager`) is the right integration point, and application code should simply receive the session rather than fetch it. A good senior answer names the strategy property, explains the transaction-scoped lifetime, and calls out thread-boundary and leak behaviour rather than reciting the two method names.
- What happens if you call getCurrentSession() with no transaction active under the 'thread' strategy?Hibernate throws HibernateException reporting that it could not obtain a transaction-synchronized session for the current thread. The contextual session's lifetime is defined by the transaction — it is flushed, closed and unbound on completion — so with no transaction there is nothing to scope it to.
- Why is submitting work to a thread pool inside a unit of work dangerous with contextual sessions?With the thread strategy the session is bound to the calling thread, so the pooled worker either has no bound session or has a stale one from earlier work. Passing your session into the worker is worse, because sessions are not thread-safe. Each asynchronous task must open its own session and transaction.
saying these in an interview costs you the question
- Closing a session obtained from getCurrentSession()
- Assuming getCurrentSession() works without an active transaction
- Caching a Session in your own ThreadLocal on pooled request threads
- Believing openSession() reuses an existing session when one is already bound
- Treating 'current session' as a Hibernate-wide singleton rather than a context-scoped binding