Hibernate Architecture

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 (JPA & ORM) already introduces the object/relational paradigm mismatch briefly. This page goes deeper into each friction point and lays out where Hibernate’s runtime objects sit relative to JDBC.

The paradigm mismatch, in depth

  • Granularity — an Address or Money class is naturally its own type in Java, but rarely its own table; it typically maps onto a handful of columns of the owning entity’s table (@Embeddable, see Basic & Embeddable Types).

  • Inheritance — Java’s single-inheritance class hierarchies and interfaces have no direct SQL counterpart. A Payment / CardPayment / BankTransferPayment hierarchy has to be flattened onto one of three table shapes — see Inheritance Mapping.

  • Identity — Java has three notions of "sameness": reference identity (==), equals()-defined equality, and database identity (same primary key value). Two separately loaded EntityManager instances can each hold a distinct Java object for the same row; a single persistence context, by contrast, guarantees one Java instance per row (the identity map, see Persistence Context & Lifecycle).

  • Associations — a Java reference is directional and single-ended; a foreign key is undirected column data navigable from either table. A @ManyToMany needs a join table that has no object-model counterpart at all. See Associations.

  • Data navigation — walking order.getCustomer().getAddress().getCity() one reference at a time is natural in Java and disastrous against a database unless the right join (or fetch strategy) accompanies it — the root cause of the N+1 problem, see Fetching & N+1.

Hibernate’s job is to let you program against the object model while it works out the SQL, joins, and identity bookkeeping needed to reconcile it with the relational model underneath.

Layered architecture

flowchart TB App["Application code"] --> API["JPA API (EntityManager) or native Hibernate API (Session)"] API --> PC["Persistence context\n(identity map + first-level cache + dirty checking)"] PC --> SQLGen["SQL generation / HQL-JPQL-Criteria translation"] SQLGen --> JDBC["JDBC driver"] JDBC --> DB[("Relational database")] API -.optional, no dirty checking / caching.-> Stateless["StatelessSession"] Stateless --> JDBC

Application code talks to either the standard JPA API (EntityManager) or Hibernate’s own native API (Session, a superset of EntityManager); both sit on top of the same persistence-context machinery, which in turn generates SQL and delegates to a plain JDBC Connection. Hibernate never talks to the database except through JDBC — there is no proprietary wire protocol.

SessionFactory/Session vs. EntityManagerFactory/EntityManager

Hibernate’s native API predates JPA and remains richer than it:

Native type JPA equivalent Notes

SessionFactory

EntityManagerFactory

One per persistence unit, thread-safe, application-scoped. sessionFactory is obtainable from entityManagerFactory.unwrap(SessionFactory.class).

Session

EntityManager

Session extends EntityManager conceptually and exposes extra operations JPA does not standardize: Session.lock() variants, HQL-specific query options, Session.getStatistics(), Filter activation, and StatelessSession access.

Query (Hibernate)

TypedQuery/Query (JPA)

Hibernate’s org.hibernate.query.Query extends the JPA one with scrolling, insertion order, and cache-region hints.

A JPA-only application never needs to see org.hibernate.* types; reaching for Session (via entityManager.unwrap(Session.class)) is a deliberate opt-in to Hibernate-specific capability, and ties that code to Hibernate as the provider.

StatelessSession

StatelessSession is a command-oriented alternative to Session: no persistence context, no first-level cache, no automatic dirty checking, no cascading, no interceptors/events by default. Every operation (insert/update/delete/get) is issued immediately as its own SQL statement, and returned entities are detached the moment they are loaded. It trades away Hibernate’s automation for predictable, low-overhead behavior — exactly what large batch/ETL jobs want; see Bulk Operations & Batching.

Persistence units and dialects

A persistence unit names a set of managed classes, a data source, and a bag of properties — the unit of configuration an EntityManagerFactory is built from (one unit per persistence.xml <persistence-unit> element, or the equivalent Spring Boot auto-configuration). A dialect (org.hibernate.dialect.Dialect and its subclasses — PostgreSQLDialect, MySQLDialect, SQLServerDialect, …​) tells Hibernate the SQL variant, pagination syntax, identity/sequence support, and type mappings for one specific database product; since Hibernate 6 the dialect is auto-detected from the JDBC connection metadata in the overwhelming majority of cases, so hibernate.dialect rarely needs to be set explicitly.