Updated August 2026 — full tutorial restored after the truncated network copy was migrated empty.
In this tutorial, I will teach you how to implement JPA Auditing in a Spring Boot application that uses MySQL. Auditing lets you track who created or changed a row, and when — without writing that boilerplate on every save.
Previously we covered:
- Part 1: How to add Login to your application using Spring Security
- Part 2: How to store username and password in a MySQL database
A closely related walkthrough (same auditing steps, slightly different framing) is also here: Auditing in Spring Boot (Step by Step).
We will cover:
- What is Auditing in Spring Data JPA?
- Create an Auditable base class
- Make your entities extend Auditable
- Implement the AuditorAware interface
- Enable JPA Auditing and register the bean
- MySQL columns and a quick test
1. What is Auditing in Spring Data JPA?
Auditing helps you track changes to a table. For example, you need to know:
- who created the record
- when it was created
- who modified the record last
- when it was last modified
Before Spring Data JPA auditing, you often added those fields to every entity and set them manually on insert/update. With auditing enabled, Spring fills them automatically when you save through a repository.
Typical fields (and the annotations that drive them):
createdBy—@CreatedBycreatedDate—@CreatedDatelastModifiedBy—@LastModifiedBylastModifiedDate—@LastModifiedDate
With MySQL, those map cleanly to VARCHAR/DATETIME (or TIMESTAMP) columns on each audited table.
2. Create an Auditable base class
Create an abstract class that holds the audit fields. Any entity you want to audit will extend this class.
Step 1: In your models package, create abstract class Auditable<U> with:
createdBycreatedDatelastModifiedBylastModifiedDate
For @Temporal(TIMESTAMP), import:
import static javax.persistence.TemporalType.TIMESTAMP;
(On Jakarta Persistence / newer Spring Boot, use jakarta.persistence instead of javax.persistence.)
Step 2: Annotate each field with the matching Spring Data annotation (@CreatedBy, @CreatedDate, etc.).
Step 3: Annotate the class with @MappedSuperclass (no table of its own).
Step 4: Annotate the class with @EntityListeners(AuditingEntityListener.class).
Step 5: Generate getters and setters (omitted below for brevity).
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
@CreatedBy
protected U createdBy;
@CreatedDate
@Temporal(TIMESTAMP)
protected Date createdDate;
@LastModifiedBy
protected U lastModifiedBy;
@LastModifiedDate
@Temporal(TIMESTAMP)
protected Date lastModifiedDate;
// getters and setters
}
Tip: on modern Spring Data you can also use LocalDateTime / Instant with @CreatedDate / @LastModifiedDate and skip @Temporal.
3. Make your entities extend Auditable
Step 1: Change entities such as Post, User, or Location to extend Auditable<String> (use String if the auditor is a username).
Step 2: Start the application so Hibernate can add the four columns to the MySQL tables (spring.jpa.hibernate.ddl-auto=update in dev, or add a Flyway/Liquibase migration in production).
Step 3: Confirm the columns exist (MySQL Workbench, DESCRIBE your_table;, or H2 console if you use H2 locally).
4. Implement the AuditorAware interface
Spring needs a bean that returns the current auditor (usually the logged-in username from Spring Security — see Parts 1 and 2).
Step 1: Create a class that implements AuditorAware<String>.
Step 2: Override getCurrentAuditor(). For a quick demo you can hard-code a name; for a real app, read the SecurityContext.
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// Demo only — replace with SecurityContextHolder in production
return Optional.ofNullable("Kindson").filter(s -> !s.isEmpty());
}
}
Production-style sketch with Spring Security:
@Override
public Optional<String> getCurrentAuditor() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || !auth.isAuthenticated()) {
return Optional.empty();
}
return Optional.ofNullable(auth.getName());
}
5. Enable JPA Auditing and register the bean
Step 1: Annotate a configuration class (often the main application class) with:
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
Step 2: Expose an AuditorAware bean:
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
@SpringBootApplication
public class RelationshipDemoApplication {
@Bean
public AuditorAware<String> auditorAware() {
return new SpringSecurityAuditorAware();
}
public static void main(String[] args) {
SpringApplication.run(RelationshipDemoApplication.class, args);
}
}
6. MySQL columns and a quick test
Example MySQL-oriented column types once Hibernate (or your migration) has run:
created_by/last_modified_by—VARCHAR(255)created_date/last_modified_date—DATETIME(6)orTIMESTAMP
Test steps:
- Launch the application connected to MySQL.
- Insert or update a row through a REST client (Postman / Insomnia) or your UI.
- Query the table and confirm the four audit fields are populated.
SELECT id, created_by, created_date, last_modified_by, last_modified_date
FROM your_table
ORDER BY id DESC
LIMIT 5;
Next steps
- Wire
AuditorAwareto the logged-in user from Spring Security login and your MySQL user store. - Prefer schema migrations over
ddl-auto=updatein production. - For a full change history (old vs new values), look at Hibernate Envers — that is beyond basic JPA auditing.