LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2421 min read

Hibernate with Spring

Learn how Spring integrates with Hibernate, the difference between LocalSessionFactoryBean and Spring Boot auto-configuration, and how Spring Data JPA wraps Hibernate underneath.

Introduction

Everything so far has used plain Hibernate: a SessionFactory built directly from hibernate.cfg.xml, manual Sessions, and manual transactions. In real Spring and Spring Boot applications, you rarely write that setup code yourself — Spring manages the SessionFactory, the EntityManager, and transaction boundaries for you. This lesson connects what you already know to how it actually looks in a Spring codebase.

What You Will Learn
  • Why Spring and Hibernate are commonly used together
  • How classic Spring wires Hibernate with LocalSessionFactoryBean
  • How Spring Boot auto-configures Hibernate as the JPA provider
  • How Spring Data JPA sits on top of Hibernate
  • How @Transactional replaces manual beginTransaction/commit

Why Combine Them?

Hibernate handles the object-relational mapping; Spring handles dependency injection, configuration, and transaction management around it. Together, Spring removes almost all the manual plumbing you have been writing by hand throughout this course — SessionFactory creation, Session lifecycle, and transaction begin/commit/rollback are all managed declaratively.

Classic Spring: LocalSessionFactoryBean

Before Spring Boot, a classic Spring XML or Java configuration built the Hibernate SessionFactory as a managed bean using LocalSessionFactoryBean, which Spring then injected wherever it was needed.

import org.springframework.context.annotation.*;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
public class HibernateConfig {
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setPackagesToScan("com.example.entities");
Properties props = new Properties();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
props.put("hibernate.hbm2ddl.auto", "update");
sessionFactory.setHibernateProperties(props);
return sessionFactory;
}
@Bean
public HibernateTransactionManager transactionManager(
org.hibernate.SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}

This approach still uses Hibernate's native Session API underneath, just with Spring handling bean creation and transaction management instead of you calling openSession() and beginTransaction() by hand.

Spring Boot Auto-Configuration

Spring Boot removes almost all of that configuration. Add spring-boot-starter-data-jpa and a database driver, set a few properties, and Spring Boot auto-configures a Hibernate-backed EntityManagerFactory for you.

application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/companydb
spring.datasource.username=root
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
JPA, Not the Native Session API

Spring Boot applications typically program against javax.persistence.EntityManager, the JPA standard interface, rather than Hibernate's native Session. Hibernate is still doing the work underneath — EntityManager is the portable, spec-defined API on top of it.

Spring Data JPA on Top

Most Spring Boot applications go one layer further and use Spring Data JPA, which generates repository implementations from an interface, so you rarely write EntityManager or Session code directly at all.

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
List<Employee> findBySalaryGreaterThan(Double amount);
}
Using the repository in a service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
public List<Employee> getHighEarners() {
return employeeRepository.findBySalaryGreaterThan(60000.0);
}
}
Hibernate SQL Log (generated behind the scenes)

Click Run to see what this code prints.

That findBySalaryGreaterThan method has no implementation anywhere in your code — Spring Data JPA parses the method name and generates a query, which Hibernate then translates into the SQL above. Everything you learned about HQL, fetch types, and caching in this course still applies underneath; Spring Data JPA is a thinner, more convenient layer on top of the same Hibernate engine.

@Transactional

Instead of manually calling beginTransaction(), commit(), and rollback() as in the previous lesson, Spring applications typically annotate a method with @Transactional, and Spring wires the transaction boundary around the whole method automatically.

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class PayrollService {
@Autowired
private EmployeeRepository employeeRepository;
@Transactional
public void giveRaise(Long employeeId, Double amount) {
Employee emp = employeeRepository.findById(employeeId)
.orElseThrow(() -> new IllegalArgumentException("Employee not found"));
emp.setSalary(emp.getSalary() + amount);
// No explicit save() call needed here inside a @Transactional method —
// Hibernate's dirty checking flushes the change automatically on commit.
}
}
Dirty Checking Still Applies

Inside a @Transactional method, the entity loaded via findById() is a managed entity within an active persistence context — exactly like a Session-managed entity in plain Hibernate. Changing its fields is enough; Hibernate detects and flushes the change when the transaction commits.

Common Mistakes

Avoid These Mistakes
  • Mixing manual Session/Transaction code into a Spring-managed application instead of relying on @Transactional.
  • Forgetting @Transactional on a write method and being surprised when dirty-checked changes are never flushed.
  • Assuming Spring Data JPA replaces the need to understand Hibernate — the underlying fetch, cache, and N+1 concerns are all identical.
  • Not setting spring.jpa.hibernate.ddl-auto deliberately, letting Hibernate auto-alter a production schema.
  • Calling a @Transactional method from within the same class and expecting the proxy-based transaction to apply — Spring's proxy only intercepts external calls.

Best Practices

  • Prefer Spring Data JPA repositories for standard CRUD, dropping to EntityManager or native queries only when needed.
  • Keep @Transactional at the service layer, wrapping a coherent unit of business logic, not scattered across the DAO layer.
  • Set spring.jpa.hibernate.ddl-auto=validate or none in production; use update only in local development.
  • Enable spring.jpa.show-sql during development to see exactly what Hibernate generates underneath Spring Data JPA.
  • Keep applying everything learned about fetch types, caching, and the N+1 problem — Spring does not change that layer.

Frequently Asked Questions

No, it is a layer on top of a JPA provider — Hibernate by default. Spring Data JPA generates repository code; Hibernate still does the actual ORM work underneath.

Yes. Fetch types, caching, the N+1 problem, and HQL/JPQL all still apply — Spring Data JPA just reduces boilerplate, it does not remove the underlying ORM concerns.

EntityManager is the standard JPA interface; Session is Hibernate's native, richer API. Hibernate's EntityManager implementation is, under the hood, backed by a Session.

Yes, by unwrapping the EntityManager to a Session, though most Spring Boot applications stick to EntityManager or Spring Data JPA repositories for consistency.

Key Takeaways

  • Spring manages the SessionFactory/EntityManagerFactory and transaction boundaries so you do not have to.
  • Classic Spring wires Hibernate explicitly with LocalSessionFactoryBean; Spring Boot auto-configures it.
  • Spring Data JPA generates repository implementations on top of Hibernate from interface method names.
  • @Transactional replaces manual beginTransaction/commit/rollback at the method level.
  • Everything learned about HQL, fetch types, and caching in this course still applies underneath Spring Data JPA.

Summary

Spring does not replace Hibernate — it automates the setup and transaction plumbing around it. Whether you see LocalSessionFactoryBean, Spring Boot auto-configuration, or a Spring Data JPA repository, Hibernate is still generating the SQL and managing entity state underneath, exactly as it has throughout this course.

Next Lesson →

Common Hibernate Exceptions & Debugging