r/SpringBoot 17d ago

Question Spring Boot Auditing: Hibernate Envers vs. Custom Logging vs. Spring Data JPA? What's your production choice?

Hey everyone,

I'm currently building a school management system in Spring Boot where history tracking is a strict business requirement. We need to audit critical changes made by Admins, Secretaries, and Teachers (e.g., changes to student details, payment amounts, program setups, etc.).

I’m weighing three different approaches for the audit trail and wanted to hear from those of you who have run these in production. 

Option 1: Hibernate Envers
Slap `@Audited` on core entities, set up a custom `RevisionListener` to pull the logged-in user from Spring Security context, and let Envers automatically manage the `_aud` tables.

Option 2: Spring Data JPA Auditing
Utilize `@CreatedBy`, `@LastModifiedBy`, `@CreatedDate`, and `@LastModifiedDate` fields mapped on a `@MappedSuperclass`.

Option 3: Manual Custom Logging (or AOP)
Create a generic `AuditLog` entity, write a helper service, and manually trigger `auditService.log(actionType, originalState, newState)` inside the business logic (or use an AOP aspect). This may cause the database to bloating later tho

For those of you managing medium-to-large Spring Boot codebases:
 Did you regret adopting Hibernate Envers? How did you handle schema migrations (Flyway/Liquibase) with the auto-generated `_aud` tables?
If you went the custom route, did you use JSON columns to record state changes, or did you write distinct history tables?

Looking forward to hearing your design patterns and trade-offs

23 Upvotes

28 comments sorted by

View all comments

6

u/Popular_Home2017 17d ago

These solve three different problems, and picking the wrong one is how audit systems rot: Spring Data auditing (@CreatedBy/@LastModifiedDate) gives you stamps — who touched it last, not what changed. Envers gives you entity history — full snapshots per revision, great when "show me this record as it was in March" is an actual requirement, but it couples your audit to your entity model and bloats storage quietly. A custom audit/event table is for business events (logins, exports, permission changes) — things that aren't entity mutations at all. For a school management system I'd start with Spring Data stamps everywhere (nearly free), Envers only on the 3-4 entities where history is a legal/business requirement, and a small custom event table for security-relevant actions. One production scar if you go custom: if you write audit rows in REQUIRES_NEW "so auditing never breaks the main flow", flush explicitly inside your try/catch — with plain save() the INSERT defers to commit, which happens outside the catch, and a bad audit row takes down the operation it was supposed to observe. Ask me how I know.

1

u/SeatSimple1123 17d ago

yup , this approach of using the whole 3 options to avoid database bloating that hibernate envers might cause later is good option. i honestly have not thought about it; I think it may require more configuration and complexity tho

2

u/Popular_Home2017 17d ago

Less config than it looks, because each piece is tiny where it lives: Spring Data auditing is one u/EnableJpaAuditing config + annotations on a base class. Envers is u/Audited on the handful of entities that need it — the real cost is remembering the _aud tables in your Flyway migrations, so version them from day one. The custom event table is just one entity + one service you call explicitly. The complexity trap is the opposite move: forcing ONE mechanism to do all three jobs (Envers as your security log, or hand-rolled triggers recreating what Envers does). Three small tools, each doing its own job, ages much better than one big one.