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

Spring Data JPA provides excellent support to transparently keep track of who created or changed an entity and the point in 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 either directly to your entity classes or by extending an abstract class that defines annotated audit fields.

Since we need auditing feature for most of the 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, you only need spring-data-starter-data-jpa and mysql-connector-java dependencies.

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-mentioned dependencies.

Configure MySQL Database

Spring Boot automatically configures the DataSource bean for in-memory databases like H2 database, HSQLDB, and Apache Derby. For the MySQL database, you have to explicitly 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 purpose - 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 as 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, makes sure that the database schema is automatically created 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 knows about these audit fields, you have to annotate the fields with @CreatedBy and @LastModifiedBy to track the user who created or updated the entity as well as @CreatedDate and @LastModifiedDate to log the time when these changes were made.

Here is how 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 @MappedSuperclass and @EntityListeners annotations. The @MappedSuperclass annotation indicates that the Auditable class 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.

Now any entity that extends the Auditable abstract class will benefit from the JPA auditing feature. Spring Data JPA will automatically manage CreatedBy, CreatedDate, LastModifiedBy, 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 respectively. 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 somehow 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 current logged in user, we have to provide the implementation of AuditorAware and override its getCurrentAuditor method, as show 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 either 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

Now, 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 have first saved several todos and then retrieved them by 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

That's all folks. In this article, you have learned what is 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 JPA auditing feature:

  1. Define the audit fields by using the @CreatedDate, @CreatedBy, @LastModifiedDate, and @LastModfiiedBy annotations. The best way to do so is by creating a generic abstract class and then extending the entities which need the auditing functionality.
  2. Implement the AuditorAware interface to let Spring Data JPA auditing infrastructure knows about the currently logged-in user who is 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 auditing author).

That's it. This article also pretty much sums up the Spring Data JPA tutorial series. If you've not read the earlier articles yet, check the links below in the further reading section.

Further Reading

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

✌️ Like this article? Follow @attacomsian on Twitter. You can also follow me on LinkedIn and DEV. Buy me a coffee (cost $3)

Need help to start a new Spring Boot or MEAN stack project? I am available for contract work. Hire me to accomplish your business goals with engineering and design. Let’s talk about your project: hi@attacomsian.com.