Hibernate & Spring Boot Integration

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.

Every other page in this section documents Hibernate on its own terms — plain Jakarta Persistence, independent of any framework. This page is the deliberate exception: it answers the question every reader arriving from the SpringBoot Reference eventually asks — what, exactly, is Spring Data JPA adding on top of Hibernate, and where does Hibernate stop and Spring start?

Two layers, not two competing choices

Spring Data JPA does not replace Hibernate — it sits on top of it. Spring Boot’s spring-boot-starter-data-jpa pulls in Hibernate ORM as the default Jakarta Persistence provider, Spring ORM, and a connection pool (HikariCP), then auto-configures an EntityManagerFactory from spring.datasource. and spring.jpa. — HibernateJpaVendorAdapter is the adapter Boot uses to plug Hibernate in as that provider and translate common settings (dialect detection, DDL mode) into Hibernate properties. Every entity mapping annotation (@Entity, @ManyToOne, @Id), every flush, every piece of dirty-checking and every SQL statement Spring Data JPA ultimately issues is Hibernate doing the actual ORM work — see Hibernate (JPA & ORM) — Spring Boot integration for that wiring in full, and Getting Started for Hibernate’s own bootstrap outside of Spring entirely.

Spring Data JPA, covered in depth in Spring Data JPA, is a repository-generation layer: it takes an interface (JpaRepository<Book, Long>) and generates an implementation at startup, backed by an EntityManager obtained from that same Hibernate-provided EntityManagerFactory. Nothing about Hibernate’s own model — the persistence context, entity states, dirty checking, caching, HQL — changes underneath it; Spring Data JPA just removes the boilerplate of writing a DAO class by hand.

Hibernate (native / plain JPA) Spring Data JPA

What it is

The ORM engine and Jakarta Persistence provider — entity mapping, the persistence context, dirty checking, caching, query execution.

A repository-generation layer built on top of a JPA provider (Hibernate, by default in Spring Boot).

How you use it

Inject/obtain an EntityManager (or a native Session) and call persist/find/createQuery yourself.

Declare a repository interface; Spring Data generates the CRUD/query implementation at startup.

Boilerplate for common CRUD

You write the EntityManager calls for every operation.

None — save/findById/findAll/delete come from CrudRepository/JpaRepository for free.

Deriving a query from intent

Not built in — write HQL/JPQL, Criteria, or native SQL by hand (HQL/JPQL, Criteria API).

Derived query methods parse a query straight out of a method name/@Query annotation — see Spring Data JPA and Spring Data Overview.

Type-safe, runtime-composed queries

The JPA Criteria API / SelectionSpecification (Criteria API).

Specifications, built on the same Criteria API underneath — no separate query engine.

Transaction demarcation

Programmatic (EntityTransaction/Session transaction API) unless something else (Spring) supplies declarative @Transactional.

@Transactional on repository/service methods, backed by Spring’s PlatformTransactionManager — Hibernate still executes the actual SQL underneath.

Auditing (created_at/updated_at/etc.)

Manual, via lifecycle callbacks (Events, Interceptors & Filters) or Envers for full history (Envers Auditing).

@CreatedDate/@LastModifiedDate/@CreatedBy/@LastModifiedBy with @EnableJpaAuditing — Hibernate’s lifecycle callbacks are what Spring Data hooks to implement this.

Testing without a framework

Bootstrap a SessionFactory/EntityManagerFactory directly — Integration Testing with Hibernate.

Needs Spring’s test support (@DataJpaTest, TestEntityManager) — Unit & Integration Testing.

Escape hatch for what the abstraction doesn’t cover

None needed — you already have the full session/EntityManager API.

Inject a raw EntityManager (@PersistenceContext), or drop to JdbcClient/JdbcTemplate — both covered on Spring Data JPA.

Reactive (non-blocking) support

Only via the separate Hibernate Reactive project, with a fundamentally different session/transaction model — Reactive & Data Repositories.

None — Spring Data JPA is JDBC-based and always blocking; Spring’s reactive stack uses R2DBC instead, with no Hibernate involved at all.

The practical guidance: reach for Spring Data JPA’s repositories for the large majority of CRUD and query-by-intent use cases — it is the lower-friction default this repo’s own Spring Data JPA page assumes. Drop to the Hibernate/JPA APIs this section documents in depth (a raw EntityManager, StatelessSession, HQL written by hand, @EntityGraph tuning) whenever a repository method can’t express what’s needed — Spring Data JPA never hides or blocks that escape hatch, since the EntityManager it hands your custom repository methods is the very same Hibernate Session underneath.

Hibernate’s limitations inside a Spring Boot application

These are constraints that come from Hibernate itself, not from Spring Data JPA’s repository layer — they apply equally whether the code in front of them is a hand-written EntityManager call or a generated repository method:

  • spring.jpa.open-in-view hides N+1 problems until production load. It defaults to true, keeping the persistence context open for the whole HTTP request so lazy associations still resolve in the view/serializer — convenient, but it holds a database connection for longer than the service method and masks N+1 query patterns that only show up as latency under real traffic. See Fetching & N+1 and Hibernate (JPA & ORM) — Spring Boot integration.

  • Hibernate is fundamentally blocking. It is built on JDBC, which is a synchronous, thread-blocking API — there is no non-blocking code path through spring-boot-starter-data-jpa/Hibernate ORM at all. A Spring WebFlux application cannot call a JpaRepository (or a plain EntityManager) from a reactive pipeline without blocking the calling thread; the only genuinely non-blocking path to a JPA-mapped domain model is the separate Hibernate Reactive project, whose session/transaction model does not ride the same Reactor Context Spring’s own reactive transactions use — see Reactive & Data Repositories and Reactive Programming with Project Reactor for the two context-propagation models side by side, and why bridging between them is not automatic.

  • The second-level cache is opt-in and easy to leave misconfigured. Unlike Spring’s own @Cacheable abstraction, Hibernate’s own entity/collection/query cache ( Second-Level Cache) needs a provider dependency and per-entity @Cache annotations; adding spring-boot-starter-cache alone does nothing for it. The two caching layers solve different problems and are easy to conflate — see the cross-reference on that page and Caching for Spring’s own abstraction.

  • Not every Hibernate setting has a first-class Spring Boot property. HibernateJpaVendorAdapter only translates the common ones (dialect detection, DDL mode); anything else — batch size, statistics, a Hibernate-specific interceptor — goes through the spring.jpa.properties.hibernate.* prefix (e.g. spring.jpa.properties.hibernate.jdbc.batch_size), which is a raw pass-through with no Boot-side validation or IDE autocompletion.

  • Schema generation is a Hibernate-only development convenience, never a Spring Boot feature. spring.jpa.hibernate.ddl-auto (Boot’s property for Hibernate’s hbm2ddl.auto) is documented in full, including why validate is the only production-safe value, on Schema Generation and Tooling. Real schema change belongs to a migration tool instead — see Evolving the Database Model for tracking the database model’s evolution with Liquibase, Mongock, or Flamingock, applied the same way regardless of whether Hibernate is used through Spring Data JPA or on its own.