Spring and Hibernate are two of the most popular frameworks in the Java ecosystem. While Spring Boot simplifies configuration with auto-setup, many enterprise applications still prefer the classic Spring (non-Boot setup) for greater control and legacy system integration.
Think of Spring as the director orchestrating dependencies and transactions, while Hibernate acts as the translator converting Java objects to database tables. Together, they create a robust persistence layer.
In this tutorial, we’ll integrate Hibernate with Spring (non-Boot setup), covering XML and JavaConfig approaches, CRUD operations, queries, and best practices.
Why Integrate Hibernate with Spring?
- Spring manages
SessionFactory
and transactions, removing boilerplate code. - Enables declarative transaction management with
@Transactional
. - Provides dependency injection for DAOs and services.
- Allows fine-grained configuration compared to Spring Boot auto-setup.
Project Setup
Maven Dependencies
Add dependencies in pom.xml
:
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.30</version>
</dependency>
<!-- Spring ORM -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.30</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.4.Final</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JPA API -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
Hibernate Configuration with Spring
You can configure Hibernate with Spring using XML or JavaConfig.
XML Configuration (applicationContext.xml)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- DataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/hibernatedb"/>
<property name="username" value="root"/>
<property name="password" value="yourpassword"/>
</bean>
<!-- SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.example.model"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Java Configuration (Spring JavaConfig)
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/hibernatedb");
ds.setUsername("root");
ds.setPassword("yourpassword");
return ds;
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setDataSource(dataSource());
factory.setPackagesToScan("com.example.model");
Properties props = new Properties();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
props.put("hibernate.hbm2ddl.auto", "update");
props.put("hibernate.show_sql", "true");
factory.setHibernateProperties(props);
return factory;
}
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}
Creating an Entity
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String department;
private double salary;
// getters and setters
}
DAO Layer
@Repository
public class EmployeeDao {
@Autowired
private SessionFactory sessionFactory;
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
public void save(Employee emp) {
getSession().persist(emp);
}
public Employee findById(Long id) {
return getSession().get(Employee.class, id);
}
public List<Employee> findAll() {
return getSession().createQuery("FROM Employee", Employee.class).list();
}
public void delete(Employee emp) {
getSession().delete(emp);
}
}
Service Layer
@Service
@Transactional
public class EmployeeService {
@Autowired
private EmployeeDao dao;
public void save(Employee emp) {
dao.save(emp);
}
public Employee getById(Long id) {
return dao.findById(id);
}
public List<Employee> getAll() {
return dao.findAll();
}
public void delete(Employee emp) {
dao.delete(emp);
}
}
Testing CRUD Operations
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
EmployeeService service = context.getBean(EmployeeService.class);
Employee emp = new Employee();
emp.setName("Alice");
emp.setDepartment("IT");
emp.setSalary(7000);
service.save(emp);
List<Employee> employees = service.getAll();
employees.forEach(e -> System.out.println(e.getName()));
}
}
Common Pitfalls & Anti-Patterns
- Forgetting Transaction Management → Causes
LazyInitializationException
. - Improper Cascade Types → May result in unintended deletes or updates.
- Eager Fetching Everywhere → Leads to memory issues. Prefer lazy loading.
- Not Closing ApplicationContext in Standalone Apps → Causes resource leaks.
Best Practices
- Use DAO layer to isolate persistence logic.
- Keep service layer transactional.
- Prefer JavaConfig over XML for maintainability.
- Monitor logs to detect N+1 query problems.
- Use second-level cache for performance optimization.
📌 Hibernate Version Notes
Hibernate 5.x
- Uses
javax.persistence
. - Transaction management integrated with Spring ORM.
- SessionFactory configured with XML or JavaConfig.
Hibernate 6.x
- Migrated to
jakarta.persistence
. - Enhanced Criteria API and SQL support.
- Cleaner integration with Spring 6.x.
- Better query plan caching and flush strategies.
Conclusion & Key Takeaways
- Spring + Hibernate (non-Boot) gives fine-grained control over configuration.
- Use
SessionFactory
managed by Spring for resource efficiency. - Organize code into DAO + Service layers for cleaner architecture.
- Always enable transaction management to prevent lazy-loading issues.
FAQ: Expert-Level Questions
Q1: What’s the difference between Hibernate and JPA?
Hibernate is a JPA implementation with advanced features.
Q2: How does Hibernate caching improve performance?
It reduces database calls by storing frequently accessed data in memory.
Q3: What are the drawbacks of eager fetching?
It loads unnecessary data, increasing memory usage and slowing queries.
Q4: How do I solve the N+1 select problem in Hibernate?
Use fetch joins, @BatchSize
, or entity graphs.
Q5: Can I use Hibernate without Spring?
Yes, Hibernate can be used standalone with SessionFactory
.
Q6: What’s the best strategy for inheritance mapping?
Depends: SINGLE_TABLE
for speed, JOINED
for normalization, TABLE_PER_CLASS
for isolation.
Q7: How does Hibernate handle composite keys?
Using @Embeddable
and @EmbeddedId
or @IdClass
.
Q8: How is Hibernate 6 different from Hibernate 5?
Hibernate 6 uses jakarta.persistence
, has better SQL generation, and improved Criteria API.
Q9: Is Hibernate suitable for microservices?
Yes, though lighter ORMs like jOOQ or MyBatis may be more efficient for small services.
Q10: When should I not use Hibernate?
Avoid Hibernate in analytics-heavy apps requiring raw SQL performance.