Spring Boot
Hibernate
Spring Interceptors
Java
ORM

How to use Spring managed Hibernate interceptors in Spring Boot?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Hibernate interceptors let you observe or alter persistence behavior around entity operations such as save, update, and delete. In Spring Boot, the key design question is not only how to register an interceptor, but how to make it Spring-managed so it can receive dependencies cleanly. The reliable pattern is to register a Spring bean and wire it into Hibernate through Boot configuration rather than constructing it manually.

What a Hibernate Interceptor Is Good For

A Hibernate interceptor is useful when you need cross-cutting persistence behavior such as:

  • audit field population
  • entity change observation
  • soft validation
  • statement inspection

It is not a replacement for domain logic. Keep business decisions in services and use interceptors only for persistence-level concerns.

Create the Interceptor as a Spring Bean

Start with a Spring-managed interceptor class.

java
1import java.io.Serializable;
2import org.hibernate.EmptyInterceptor;
3import org.springframework.stereotype.Component;
4
5@Component
6public class AuditInterceptor extends EmptyInterceptor {
7
8    @Override
9    public boolean onSave(
10            Object entity,
11            Serializable id,
12            Object[] state,
13            String[] propertyNames,
14            org.hibernate.type.Type[] types) {
15
16        for (int i = 0; i < propertyNames.length; i++) {
17            if ("createdBy".equals(propertyNames[i]) && state[i] == null) {
18                state[i] = "system";
19                return true;
20            }
21        }
22        return false;
23    }
24}

Because this is a Spring bean, it can also use injected collaborators if needed.

Register It with Hibernate

The simplest Boot-level registration uses Hibernate properties.

java
1import java.util.Map;
2import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.Configuration;
5
6@Configuration
7public class HibernateInterceptorConfig {
8
9    @Bean
10    HibernatePropertiesCustomizer hibernatePropertiesCustomizer(AuditInterceptor interceptor) {
11        return (Map<String, Object> properties) ->
12                properties.put("hibernate.session_factory.interceptor", interceptor);
13    }
14}

This keeps the interceptor under Spring control while letting Hibernate use it globally.

Inject Spring Dependencies Safely

Once the interceptor is a bean, dependency injection works normally.

java
1import org.springframework.stereotype.Component;
2
3@Component
4public class CurrentUserProvider {
5    public String currentUser() {
6        return "system";
7    }
8}

Then inject it:

java
1@Component
2public class AuditInterceptor extends EmptyInterceptor {
3    private final CurrentUserProvider currentUserProvider;
4
5    public AuditInterceptor(CurrentUserProvider currentUserProvider) {
6        this.currentUserProvider = currentUserProvider;
7    }
8}

That is the core benefit of using a Spring-managed interceptor instead of new-ing it inside configuration.

Keep the Interceptor Lightweight

Interceptors run inside persistence operations, so keep them fast and predictable. Avoid:

  • network calls
  • heavy repository usage
  • complex branching
  • side effects that can trigger more persistence unexpectedly

A slow interceptor can degrade every write path in the application.

Alternative: Event Listeners Versus Interceptors

Sometimes a Hibernate event listener or JPA entity listener is a better fit. Use an interceptor when you want a broader Hibernate-level hook. Use entity-specific listeners when the behavior belongs to one entity type.

Choosing the smallest hook that solves the problem usually gives simpler code and fewer surprises.

Testing the Registration

A practical integration test should persist an entity and assert that the interceptor changed state as expected.

For example:

  • save entity without createdBy
  • flush transaction
  • assert stored value is system

This verifies both the interceptor logic and the Boot registration path.

Common Pitfalls

  • Constructing the interceptor manually and losing Spring injection.
  • Putting domain business logic inside a persistence interceptor.
  • Making the interceptor too heavy for hot database paths.
  • Using an interceptor when an entity listener would be simpler.
  • Forgetting to integration-test that Hibernate actually registered the bean.

Summary

  • Make the interceptor a Spring bean first.
  • Register it with Hibernate through Boot configuration, not manual instantiation.
  • Keep interceptor logic lightweight and persistence-focused.
  • Inject collaborators only when truly necessary.
  • Use integration tests to verify the interceptor is active in real persistence flows.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.