September 18, 2026

JPA Auditing in Spring Boot Using MySQL (Tracking Changes)

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:

A closely related walkthrough (same auditing steps, slightly different framing) is also here: Auditing in Spring Boot (Step by Step).

We will cover:

  1. What is Auditing in Spring Data JPA?
  2. Create an Auditable base class
  3. Make your entities extend Auditable
  4. Implement the AuditorAware interface
  5. Enable JPA Auditing and register the bean
  6. 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@CreatedBy
  • createdDate@CreatedDate
  • lastModifiedBy@LastModifiedBy
  • lastModifiedDate@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:

  • createdBy
  • createdDate
  • lastModifiedBy
  • lastModifiedDate

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_byVARCHAR(255)
  • created_date / last_modified_dateDATETIME(6) or TIMESTAMP

Test steps:

  1. Launch the application connected to MySQL.
  2. Insert or update a row through a REST client (Postman / Insomnia) or your UI.
  3. 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 AuditorAware to the logged-in user from Spring Security login and your MySQL user store.
  • Prefer schema migrations over ddl-auto=update in production.
  • For a full change history (old vs new values), look at Hibernate Envers — that is beyond basic JPA auditing.

Kindson Munonye

Kindson Munonye is a software engineer and technical author covering machine learning, statistics, REST APIs, Python, and software engineering. He publishes free tutorials on The Genius Blog and live classes on Alkademy. GitHub · LinkedIn · About · Alkademy

View all posts by Kindson Munonye →
0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted