Auditing helps us track and log the persistence layer changes made by the user in an application. By using auditing, we can determine who created or updated the entity record or when it happened. In short, we keep track of every action that changes the entity state, like insert, update, and delete operations.

Spring Data JPA provides excellent support to track who created or changed an entity and the time this happened.

To enable the auditing feature in Spring Boot, we can make use of Spring Data JPA's @CreateDate, @CreatedBy, @LastModifiedDate, and @LastModifiedBy annotations. You can add these annotations directly to your entity classes or by extending an abstract class that defines annotated audit fields.

Since we need an auditing feature for most entities, in this article, we will create a generic Auditable abstract class with audit fields. Any entity can later extend this abstract class to enable the auditing functionality.

Dependencies

To use Spring Data JPA with the MySQL database in a Spring Boot application, spring-data-starter-data-jpa and mysql-connector-java dependencies are required.

Add the following dependencies to your Gradle project's build.gradle file:

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'

For Maven, include the following dependencies to your pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

If you are starting a new Spring Boot project, just use Spring Initializr web tool to bootstrap a new application with the above dependencies.

Configure MySQL Database

Spring Boot automatically configures the DataSource bean for in-memory databases like H2 database, HSQLDB, and Apache Derby. For a MySQL database, you need to specify the database connection details in a properties file.

Open the application.properties file and copy and paste the following properties:

# MySQL connection properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=mypass
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?createDatabaseIfNotExist=true&useSSL=false

# Log JPA queries
# Comment this in production
spring.jpa.show-sql=true

# Drop and create new tables (create, create-drop, validate, update)
# Only for testing purposes - comment this in production
spring.jpa.hibernate.ddl-auto=create

# Hibernate SQL dialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

Spring Boot will automatically configure DataSource based on the above properties. Make sure you change the spring.datasource.username and spring.datasource.password properties per your MySQL database installation.

The hibernate property spring.jpa.hibernate.ddl-auto = create will automatically create database tables based on the entity classes when the application starts.

The createDatabaseIfNotExist=true configuration property, included in spring.datasource.url, automatically creates the database schema if it doesn't already exist.

Create Auditable Abstract Class

Let us now create an abstract Auditable class with the createdBy, createdDate, lastModifiedBy, and lastModifiedDate properties. This generic class acts as a base class with all the common auditing fields for the child entities.

To let the Spring Boot know about these audit fields, you have to annotate the fields with @CreatedBy and @LastModifiedBy to track the user who created or updated the entity, @CreatedDate and @LastModifiedDate to log the time when these changes were made.

Here is what our Auditable abstract class looks like:

Auditable.java

package com.attacomsian.jpa.domains;

import org.springframework.data.annotation.*;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.util.Date;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<T> {

    @CreatedBy
    protected T createdBy;

    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    protected Date createdDate;

    @LastModifiedBy
    protected T lastModifiedBy;

    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    protected Date lastModifiedDate;

    public T getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(T createdBy) {
        this.createdBy = createdBy;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public T getLastModifiedBy() {
        return lastModifiedBy;
    }

    public void setLastModifiedBy(T lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }
}

As you can see above, the Auditable class is also annotated with the @MappedSuperclass and @EntityListeners annotations. The @MappedSuperclass annotation indicates that the Auditable is only a superclass and is not a JPA entity.

The @EntityListeners annotation is used to configure a JPA entity listener AuditingEntityListener to capture auditing information on persisting and updating entities. This entity listener class contains callback methods (annotated with @PrePersist and @PreUpdate) to persist and update audit fields when there is any create or update activity on the entity.

Any entity that extends the Auditable abstract class will benefit from the JPA auditing feature. Spring Data JPA will automatically manage the CreatedBy, CreatedDate, LastModifiedBy, and LastModifiedDate columns when the entity is persisted or updated.

Create an Entity

The next step is to create a Todo entity class and then extend it from the Auditable abstract class to add the auditing functionality:

Todo.java

package com.attacomsian.jpa.domains;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "todos")
public class Todo extends Auditable<String> implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private boolean completed;

    public Todo() {
    }

    public Todo(String title, boolean completed) {
        this.title = title;
        this.completed = completed;
    }

    // getters and setters, equals(), toString() .... (omitted for brevity)
}

The Todo class is annotated with the Entity annotation to indicate that it is a JPA entity. The @Table annotation is used to specify the name of the database table that should be mapped to this entity.

The id attribute is annotated with both @Id and @GeneratedValue annotations. The former indicates that it is a primary key of the entity. The latter defines the primary key generation strategy. In the above case, we have declared the primary key as an AUTO INCREMENT field.

Auditing Author with AuditorAware

We use the @CreatedDate and @LastModifiedDate annotations for tracking created and last modified dates. Spring Data JPA automatically updates these fields by taking the current system time.

But how to tell the auditing infrastructure about the author who made these changes? It needs to know this information since we have defined the @CreatedBy and @LastModifiedBy annotations in our Auditable abstract class.

To tell the auditing infrastructure about the currently logged-in user, we have to provide the implementation of AuditorAware and override its getCurrentAuditor method, as shown below:

EntityAuditorAware.java

package com.attacomsian.jpa.domains;

import org.springframework.data.domain.AuditorAware;

import java.util.Optional;

public class EntityAuditorAware implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        return Optional.of("Atta");
    }
}

Note: For the sake of simplicity, I am returning a hard-coded auditor name in the getCurrentAuditor() method. For real-world applications with Spring Security, you need to find the current logged-in user and return it back.

Create a Repository

Let us now create the TodoRepository interface to save and retrieve Todo entities from the database as follows:

TodoRepository.java

package com.attacomsian.jpa.repositories;

import com.attacomsian.jpa.domains.Todo;
import org.springframework.data.repository.CrudRepository;

public interface TodoRepository extends CrudRepository<Todo, Long> {

    // TODO: add queries
}

We're extending TodoRepository from Spring Data JPA's CrudRepository interface to inherit the standard CRUD methods for creating, reading, updating, and deleting Todo entities.

Enable JPA Auditing

Finally, we need to enable the JPA auditing feature by specifying @EnableJpaAuditing on one of our configuration classes. We also need to define a bean of type AuditorAware and return an instance of the EntityAuditorAware class.

You can create a separate configuration class or use the main application class to define these configurations. Let us create the AuditConfiguration class to let the Spring Data JPA knows we want to enable auditing:

AuditConfiguration.java

package com.attacomsian.jpa.config;

import com.attacomsian.jpa.domains.EntityAuditorAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class AuditConfiguration {

    @Bean
    public AuditorAware<String> auditorAware() {
        return new EntityAuditorAware();
    }
}

That's all you need to do to enable Spring Data JPA auditing functionality in your Spring Boot and MySQL application. Let us now create the main application class to test our implementation.

Testing the Application

It is time to create the main application class for our Spring Boot project. This class also exposes a bean of type CommandLineRunner that defines a run() method, which is invoked by Spring Boot after the application context has been loaded.

Application.java

package com.attacomsian.jpa;

import com.attacomsian.jpa.domains.Todo;
import com.attacomsian.jpa.repositories.TodoRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.Arrays;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner auditingDemo(TodoRepository todoRepository) {
        return args -> {

            // create new todos
            todoRepository.saveAll(Arrays.asList(
                    new Todo("Buy milk", false),
                    new Todo("Email John", false),
                    new Todo("Visit Emma", false),
                    new Todo("Call dad", true),
                    new Todo("Weekend walk", true),
                    new Todo("Write Auditing Tutorial", true)
            ));

            // retrieve all todos
            Iterable<Todo> todos = todoRepository.findAll();

            // print all todos
            todos.forEach(System.out::println);
        };
    }
}

As you can see above, we first saved several todos and then retrieved them using the findAll() method. The last statement prints all todos on the console.

To see the actual output, you need to run the application. If you are using Gradle, execute the following command to start the application:

$ ./gradlew bootRun

For Maven, type the following command to launch the application:

$ ./mvnw spring-boot:run

Once the application is started, you should see the following output:

Todo{id=1, title='Buy milk', completed=false, createdBy=Atta, createdDate=2019-10-21 03:01:25.0, lastModifiedBy=Atta, lastModifiedDate=2019-10-21 03:01:25.0}
Todo{id=2, title='Email John', completed=false, createdBy=Atta, createdDate=2019-10-21 03:01:25.0, lastModifiedBy=Atta, lastModifiedDate=2019-10-21 03:01:25.0}
Todo{id=3, title='Visit Emma', completed=false, createdBy=Atta, createdDate=2019-10-21 03:01:25.0, lastModifiedBy=Atta, lastModifiedDate=2019-10-21 03:01:25.0}
Todo{id=4, title='Call dad', completed=true, createdBy=Atta, createdDate=2019-10-21 03:01:25.0, lastModifiedBy=Atta, lastModifiedDate=2019-10-21 03:01:25.0}
Todo{id=5, title='Weekend walk', completed=true, createdBy=Atta, createdDate=2019-10-21 03:01:25.0, lastModifiedBy=Atta, lastModifiedDate=2019-10-21 03:01:25.0}
Todo{id=6, title='Write Auditing Tutorial', completed=true, createdBy=Atta, createdDate=2019-10-21 03:01:25.0, lastModifiedBy=Atta, lastModifiedDate=2019-10-21 03:01:25.0}

Source Code: Download the complete source code from GitHub available under MIT license.

Conclusion

In this article, you have learned about Spring Data JPA auditing and how to enable it in a Spring Boot and MySQL application.

In short, all you need to do is the following to enable the JPA auditing feature:

  1. Define the audit fields using the @CreatedDate, @CreatedBy, @LastModifiedDate, and @LastModfiiedBy annotations. The best way to do so is by creating a generic abstract class and extending the entities that need the auditing functionality.
  2. Implement the AuditorAware interface to let Spring Data JPA auditing infrastructure know about the currently logged-in user making the changes.
  3. Add the @EnableJpaAuditing annotation to any configuration class to enable JPA auditing.
  4. Expose a bean of type AuditorAware (only required if you need an auditing author).

Further Reading

To learn more about Spring Data JPA, check out the following articles:

✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.