Hibernate Architecture — SessionFactory, Session, EntityManager
Every Hibernate operation flows through one of two objects with a deliberately asymmetric cost: an expensive, one-time factory, and a cheap, per-request session — understanding this split is foundational to everything from here on.
Learning objectives
- Explain the cost and thread-safety difference between SessionFactory/EntityManagerFactory and Session/EntityManager.
- Describe how Spring Boot manages this lifecycle transparently via @Transactional.
- Identify a manual session-management mistake (sharing across threads, recreating the factory per request) and explain why it's wrong.
Every single database operation in the rest of this course happens through one of the two objects this chapter introduces. Before you can understand the persistence context, entity lifecycle, or transactions, you need to know exactly what a "Session" or "EntityManager" actually IS, and — crucially — how expensive (or cheap) each one is to create.
📖 Story
Think about a hotel's front desk. Setting the front desk up in the first place — hiring staff, installing the booking software, wiring the phone system — is slow and expensive, and it happens exactly ONCE, when the hotel opens. But each individual guest's interaction with that front desk — checking in, asking for a late checkout — is quick, cheap, and handled fresh for every single guest, then forgotten once they leave.
Hibernate has exactly this two-tier cost structure, and mixing the two up is a real, common mistake. Here's the wrong way to write it:
// DON'T do this — recreates the "hotel" itself on every single request public Customer findCustomer(Long id) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit"); EntityManager em = emf.createEntityManager(); Customer c = em.find(Customer.class, id); em.close(); emf.close(); return c; }
That EntityManagerFactory — the expensive, "set up the whole hotel" object — is being rebuilt from scratch on every single call. Here's the right way:
// The factory (the "hotel") is built ONCE, at application startup private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit"); // Each request just checks in and out with a fresh, cheap EntityManager public Customer findCustomer(Long id) { EntityManager em = emf.createEntityManager(); Customer c = em.find(Customer.class, id); em.close(); return c; }
SessionFactory (Hibernate's native name) / EntityManagerFactory (JPA's standard name) — a heavyweight, thread-safe object, built ONCE per application, holding your parsed entity mappings and connection pool setup. Session (Hibernate) / EntityManager (JPA) — a lightweight, NOT thread-safe object representing one single unit of work, meant to be created fresh and thrown away per request or per transaction.
You'll mostly see the JPA-standard names (EntityManagerFactory, EntityManager) in this course, since that's what Spring Boot exposes — but underneath, when Hibernate is your provider, it's really Hibernate's SessionFactory/Session doing the work. They map onto each other one-to-one.
In Spring Boot, you never actually write the code from this chapter's story
Here's the good news: Spring Boot's dependency injection does all of that factory-then-fresh-session dance for you, automatically:
@Service public class CustomerService { @PersistenceContext private EntityManager entityManager; // Spring hands you a fresh, request-scoped one @Transactional public Customer findCustomer(Long id) { return entityManager.find(Customer.class, id); // No manual open/close — Spring manages the EntityManager's // entire lifecycle for you, tied to this method's transaction. } }
There's exactly ONE EntityManagerFactory built for your whole application, at startup — you never see it directly, but it's there, doing the expensive "build the hotel" work once. Every time a @Transactional method like the one above runs, Spring quietly asks that one factory for a fresh EntityManager, uses it for the duration of the method, and discards it when the method finishes.
Why the cost difference matters, concretely
The factory holds real, expensive state: every entity's parsed mappings, the whole connection pool configuration. Building this takes real time — you want it to happen exactly once. An EntityManager, by contrast, is genuinely cheap and disposable, which is exactly why Spring can afford to create a fresh one for every single request without any performance concern.
Thread safety: the other reason this split exists
The factory is safely shareable across every thread in your application — many requests can ask it for their own EntityManager simultaneously, with no conflict. An individual EntityManager, though, is explicitly NOT safe to share across threads — which is exactly why Spring gives each request its OWN one, rather than one shared instance for everybody.
When your Spring Boot app starts, it builds exactly one EntityManagerFactory — this is where Hibernate parses every one of your @Entity classes' mappings and sets up the connection pool (usually HikariCP, covered in a later chapter). For each incoming request that needs the database, Spring's @Transactional machinery quietly asks that one factory for a fresh EntityManager — which wraps ONE physical database connection for the life of that request — and discards it (returning the connection to the pool) once the transaction finishes.
- Every Spring Boot
@Repository/@Transactionalmethod you'll ever write relies entirely on this factory-once, entity-manager-per-request pattern, completely transparently — you're using it right now in this course's examples, whether you've noticed or not. - A batch job manually managing its own sessions (outside Spring's automatic request-scoping) is one of the rare places you'd write the manual open/close code from this chapter's story yourself — and getting the lifecycle wrong there is a classic source of memory growth.
- An application reading from two genuinely separate databases needs two separate
EntityManagerFactoryinstances — one per datasource — a real, deliberate configuration decision some larger Spring Boot applications need to make.
- Let Spring manage the
EntityManagerFactory/EntityManagerlifecycle for you via@Transactionalin the vast majority of cases — you saw exactly how simple that looks in this chapter's Concept Overview. - If you ever DO manage a session manually (a background job, say), never share one
EntityManager/Sessionacross threads — create a fresh one per unit of work, exactly the way Spring does for you automatically. - Never recreate the factory itself per request — that rebuilds all the expensive parsed-mapping work from scratch, on every single call, exactly like the "wrong way" code in this chapter's story.
⚠️ Why this keeps happening
Spring Boot's auto-configuration hides this entire factory/session split so well that most developers never see an EntityManagerFactory directly — which is exactly why, the one time someone DOES manage sessions manually (a batch job, say), the two most common mistakes from this chapter's story tend to resurface.
- Manually recreating the
EntityManagerFactoryper operation, in hand-rolled (non-Spring) code — exactly the "DON'T do this" example from this chapter's Problem Statement, paying the expensive setup cost over and over. - Sharing one
EntityManager/Sessionacross multiple threads, leading to unpredictable, hard-to-reproduce bugs, since it was never designed to be thread-safe. - Holding a
Session/EntityManageropen for far longer than its real unit of work — an entire long-running batch job, for instance — which the very next chapter explains is a genuine memory problem, not just bad style.
The factory's one-time startup cost is fixed and expected — not something to optimize away, just something to make sure only happens once. The real cost worth watching is what accumulates DURING a unit of work (the persistence context) — the very next chapter is entirely about exactly that.
No direct security concern in the factory/session split itself, but connection pool configuration (managed at the factory level) has real security-adjacent implications — pool exhaustion, covered in a later production-engineering chapter, can be triggered by a client hammering an endpoint, functioning as a denial-of-service vector if pool sizing isn't configured deliberately.
Watch your connection pool's active/idle connection counts as a direct signal of whether your application's actual EntityManager usage matches its intended per-request scope — a pool that's frequently near exhaustion under normal load often means sessions are being held open longer than intended.
Confirm (via your startup logs) that your application builds exactly one EntityManagerFactory per datasource, at startup — seeing that expensive metadata-parsing step happen again DURING normal request handling, not just once at boot, is a clear sign something's misconfigured.
- Add logging or a debugger breakpoint to watch Hibernate build the
EntityManagerFactoryexactly once, at startup — not once per request. - In a
@Transactionalmethod, inject anEntityManagerand callentityManager.unwrap(Session.class)to confirm you can reach the underlying HibernateSessiondirectly. - Write a small, non-Spring Java program that manually creates an
EntityManagerFactoryand anEntityManager, performs a save, and closes both explicitly — feel the manual lifecycle Spring normally hides from you. - In a throwaway test, deliberately try sharing one
EntityManageracross two threads doing concurrent work, and observe the resulting instability firsthand.
✓ Quick recap
EntityManagerFactory(JPA) /SessionFactory(Hibernate) is heavyweight, thread-safe, and built exactly once per application.EntityManager(JPA) /Session(Hibernate) is lightweight, NOT thread-safe, and created fresh for every single unit of work.- Spring Boot's
@Transactional+ dependency injection handles this entire lifecycle for you transparently — you rarely touch the factory directly. - Never share an
EntityManager/Sessionacross threads, and never rebuild the factory per request — the whole point of this architecture is that cost asymmetry.
Want a visual for this concept?
Generate a diagram tailored to “Hibernate Architecture — SessionFactory, Session, EntityManager” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →