LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 720 min read

SessionFactory & Session

Understand SessionFactory as an expensive, app-wide object and Session as a lightweight, per-operation unit of work.

Introduction

SessionFactory and Session are easy to confuse when you are new to Hibernate, partly because their names sound similar. This lesson focuses specifically on the difference between them, why that difference matters, and the correct way to manage each one's lifecycle.

What You Will Learn
  • Why SessionFactory is expensive to build and how to build it only once.
  • The singleton pattern commonly used to hold a SessionFactory.
  • Why Session is lightweight and scoped to a single unit of work.
  • How a Session's lifecycle typically looks from open to close.

SessionFactory: Expensive and Shared

Building a SessionFactory involves parsing all of your entity mappings, validating them, building internal metadata caches, and preparing a connection pool. This work is genuinely expensive — often taking a noticeable amount of time compared to typical method calls.

Expensive to Create

Because building a SessionFactory does real, non-trivial work, it should happen exactly once per application (per database), not once per request or per operation.

The upside of that upfront cost is that SessionFactory is thread-safe and immutable once built — many threads can safely call sessionFactory.openSession() concurrently without any coordination.

Creating a Singleton SessionFactory

The standard pattern is a small utility class that builds the SessionFactory exactly once, in a static initializer, and exposes it through a static getter.

HibernateUtil.java
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
return new Configuration()
.configure("hibernate.cfg.xml")
.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
}

Every other class in the application calls HibernateUtil.getSessionFactory() to obtain the shared factory, rather than building its own.

Session: Lightweight and Short-Lived

Unlike SessionFactory, opening a Session is cheap — it does not open a new physical database connection immediately in most configurations, and it holds far less internal state. Sessions are meant to be created frequently and discarded quickly.

A short-lived Session
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction tx = session.beginTransaction();
Student student = session.find(Student.class, 1L);
System.out.println(student.getName());
tx.commit();
}
Generated SQL

Click Run to see what this code prints.

Note

Session implements AutoCloseable, so it works cleanly with try-with-resources, guaranteeing it is closed even if an exception occurs.

The Session as a Unit of Work

A "unit of work" is a term for a group of related operations that logically belong together — for example, everything needed to process one incoming web request, or one background job run. A Session is scoped to exactly one such unit of work.

ContextTypical Session Scope
Web applicationOne Session per HTTP request.
Batch jobOne Session per batch or per chunk of records.
Desktop applicationOne Session per user action or operation.

A Session also is not thread-safe — it must be confined to the single thread that opened it, and never passed between threads or reused concurrently.

Session Lifecycle Diagram

  • 1. sessionFactory.openSession() — a new Session is created.
  • 2. session.beginTransaction() — a Transaction starts.
  • 3. Entity operations run (persist, find, merge, remove).
  • 4. transaction.commit() — pending changes are flushed to the database.
  • 5. session.close() — the Session releases its resources.

Common Mistakes

Avoid These Mistakes
  • Building a new SessionFactory inside a frequently called method, instead of reusing one shared instance.
  • Keeping a single Session open for the entire lifetime of an application, causing stale data and memory growth.
  • Sharing one Session between multiple threads, which can cause unpredictable, hard-to-reproduce bugs.
  • Forgetting to close a Session, leading to leaked resources over time.

Best Practices

  • Build the SessionFactory once, in a dedicated utility class or through your framework's dependency injection.
  • Open and close a Session around each individual unit of work, using try-with-resources.
  • Never pass a Session across thread boundaries.
  • Call sessionFactory.close() only once, during application shutdown.

Frequently Asked Questions

Not necessarily — many configurations acquire the physical connection lazily, only when the first actual database operation runs, and return it to the pool after the transaction completes.

Yes, but only when connecting to genuinely separate databases. For a single database, one shared SessionFactory is correct.

Hibernate throws an exception — a closed Session cannot be reused and a new one must be opened.

No. A Session is a Hibernate-level abstraction that manages one or more underlying JDBC connections and tracks entity state during a unit of work.

Key Takeaways

  • SessionFactory is expensive to build and should exist once per application.
  • Session is lightweight, cheap to create, and scoped to one unit of work.
  • Sessions are not thread-safe and must stay confined to a single thread.
  • A typical Session lifecycle is: open, begin transaction, do work, commit, close.

Summary

SessionFactory and Session play very different roles: one built once and shared everywhere, the other created and discarded constantly. With this distinction clear, the next lesson puts everything together by mapping your first real entity and saving a row to the database.

Next Lesson →

Your First Hibernate Entity