← All posts

Spring Boot + JPA + Audit Listener

Say you want to keep some simple audit data on your records, like:

  • create date

  • update date

  • create by

  • update by

Good news: Spring Boot already gives us a clean way to do this. Let me show you how I set it up.

Step 0 — Make sure you have JPA in your pom.xml

First things first, you need the JPA starter:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Step 1 — Create Auditor.java

This little class tells Spring who the current user is, so it can fill in the “create by” and “update by” fields:

import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Optional;

public class Auditor implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();

        return authentication != null ? Optional.of((String) authentication.getPrincipal()) : Optional.of("0");
    }

}

It’s depends on your Principal object type you use. This example use String to represent user id as Principal

Step 2 — Turn on auditing in your application class

Add @EnableJpaAuditing and define the audit bean in your Spring Boot application class:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "auditor")
public class MyAwesomeApplication {
    ...
    @Bean
    public AuditorAware<String> auditor() {
        return new Auditor();
    }
    ...
}

Step 3 — Add the listener to your base entity

Add @EntityListeners and @MappedSuperclass to a base entity, and drop in the audit fields:

import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.time.Instant;

@Data
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseEntity {
    ...
    @CreatedBy
    @Column(length = 36)
    private String createdBy;

    @CreatedDate
    private Instant created;

    @LastModifiedBy
    @Column(length = 36)
    private String updatedBy;

    @LastModifiedDate
    private Instant updated;
    ...
}

Step 4 — Extend BaseEntity

Now any entity that extends BaseEntity gets those audit fields for free:

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@EqualsAndHashCode(callSuper = true)
@Data
@Entity(name = "m_user")
public class User extends BaseEntity {

    @Column(length = 60, nullable = false, unique = true)
    private String username;

}

That’s all there is to it. From now on, whenever you save through JPA, Spring Boot fills in the audit data for you automatically.