Spring Data JPA Auditing using AuditorAware


In this article, we’ll explore the Spring Data JPA Auditing using AuditorAware. In every business application, we need to audit every change happening in data like insert, update, and delete operation. Spring Data JPA auditing helps us in various ways to track user activities. To enable auditing in your application you must add @EnableJpaAuditing to configuration class.

Spring Data JPA framework provides an abstract layer over traditional JPA by extending it. Spring Data JPA improves the implementation of data access layers by reducing the developer’s effort. You just write repository interfaces; custom finder methods and Spring will provide implementation automatically.

1. What we’ll build

In this tutorial, we are going to audit UserEntity and maintain it’s createdDate, createdBy, lastModifiedDate, and lastModifiedBy data.

2. What we’ll need

  • About 20 minute
  • JDK 1.8 or later
  • Spring Boot 2.2.2.RELEASE
  • Spring Data JPA 2.2.3.RELEASE
  • Gradle 4+ or Maven 3.2+
  • MySQL Database
  • Your favorite IDE:
    • Spring Tool Suite (STS)
    • Eclipse
    • IntelliJ IDEA

3. Dependencies Required

Here is the pom.xml file including the required dependencies used in this project.

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

4. Project Structure

The final project structure of our application in STS 4 IDE will look like as follows:

Spring Data JPA Auditing using AuditorAware

5. Entities

5.1 AuditEntity

First, we create AuditEntity abstract class which will be superclass to all our entity and holds data like id, createdDate, createdBy, lastModifiedBy, and lastModifiedDate.

AuditingEntityListener class provides call-back methods that are used to update and persist information on updating or persisting entities and @EntityListeners specify listener classes and to register our AuditingEntityListener class.

AuditEntity.java
package org.websparrow.entity;

import java.util.Date;

import javax.persistence.*;

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

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditEntity {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	protected Long id;
	@CreatedBy
	protected String createdBy;
	@CreatedDate
	@Temporal(TemporalType.TIMESTAMP)
	protected Date createdTime;
	@LastModifiedBy
	protected String lastModifiedBy;
	@LastModifiedDate
	@Temporal(TemporalType.TIMESTAMP)
	protected Date lastModifiedDate;
	
	// Generate Getters and Setters...
}

Spring data annotations used:

  • @CreatedDate: represents the date when the field was created.
  • @CreatedBy: represents the principal(user) that created the entity.
  • @LastModifiedDate: represents the date the entity was recently modified.
  • @LastModifiedBy: represents the principal(user) that recently modified the entity.
  • @MappedSuperclass: represents that this class is parent class and will be extended by other audited entities.

Note: @MappedSuperclass is not an entity in JPA. This class will not be associated with any table. The child class which inherits @MappedSuperclass annotated class will persist properties of parent class and will be associated with mapped table via using @Entity annotation.

5.2 UserEntity

UserEntity will extend AuditEntity and persists its information to the mapped table.

UserEntity.java
package org.websparrow.entity;

import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

@Entity
@Table(name = "user")
@DynamicUpdate
@DynamicInsert
public class UserEntity extends AuditEntity {

	private String firstName;
	private String lastName;

	// Generate Getters and Setters...
}

6. Auditing Author using AuditorAware

JPA needs information about currently logged in user so for that we need to implement the AuditorAware interface and override its getCurrentAuditor() method to fetch current logged in user. JPA can audit the created date and modified date by using the system’s current time but for auditor information.

AuditAware.java
package org.websparrow.config;

import java.util.Optional;

import org.springframework.data.domain.AuditorAware;

public class AuditAware implements AuditorAware<String> {

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

Here we have hardcoded logged in user for simplicity, but if you use Spring Security you can use the below class to get current logged in user.

package org.websparrow.config;

import java.util.Optional;

import org.springframework.data.domain.AuditorAware;

public class SpringSecurityAuditorAware implements AuditorAware<String> {

	public Optional<String> getCurrentAuditor() {

		Authentication authentication = SecurityContextHolder.getContext()
				.getAuthentication();

		if (authentication == null || !authentication.isAuthenticated()) {
			return null;
		}

		return ((MyUserDetails) authentication.getPrincipal()).getUsername();
	}
}

7. @EnableJPAAuditing

Now we will add @EnableJpaAuditing to configuration class and create a bean of AuditAware and provide its reference to @EnableJpaAuditing.

AuditConfiguration.java
package org.websparrow.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorRef")
public class AuditConfiguration {

	@Bean
	public AuditAware auditorRef() {
		return new AuditAware();
	}
}

8. Controller

Create an UserController class to access the application. For time being, we have hardcoded the user’s details. createUser() method insert the two users into the database and updateUser() update user based on the passed user id.

UserController.java
package org.websparrow.controller;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.websparrow.entity.UserEntity;
import org.websparrow.repo.UserRepository;

@RestController
@RequestMapping("/users")
public class UserController {

	@Autowired
	private UserRepository userRepository;

	@PostMapping
	public List<UserEntity> createUser() {
		// First User
		UserEntity userEntity1 = new UserEntity();
		userEntity1.setFirstName("Manish");
		userEntity1.setLastName("Fartiyal");

		// Second User
		UserEntity userEntity2 = new UserEntity();
		userEntity2.setFirstName("Atool");
		userEntity2.setLastName("Ray");

		List<UserEntity> userList = Arrays.asList(userEntity1, userEntity2);

		System.out.println("User created");

		return userRepository.saveAll(userList);

	}

	@PutMapping
	public UserEntity updateUser() {

		// Update User
		UserEntity userEntity = userRepository.findById(2L).get();
		userEntity.setFirstName("Atul");
		userEntity.setLastName("Sharma");

		System.out.println("User updated");
		return userRepository.save(userEntity);
	}
}

9. application.properties

application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/spring_prod
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

10. Start the application

package org.websparrow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringDataAuditingApp {

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

11. Test the application

To test the application, start the application and hit the following endpoint in the Postman client. As we have already hardcoded logged in user “lucifer” in AuditAware class.

11.1 Creating new users:

Endpoint: http://localhost:8080/users

HTTP Method: POST

You will get below response in you the Postman client.

[
    {
        "id": 1,
        "createdBy": "lucifer",
        "createdTime": "2020-06-27T12:28:48.583+0000",
        "lastModifiedBy": "lucifer",
        "lastModifiedDate": "2020-06-27T12:28:48.583+0000",
        "firstName": "Manish",
        "lastName": "Fartiyal"
    },
    {
        "id": 2,
        "createdBy": "lucifer",
        "createdTime": "2020-06-27T12:28:48.651+0000",
        "lastModifiedBy": "lucifer",
        "lastModifiedDate": "2020-06-27T12:28:48.651+0000",
        "firstName": "Atool",
        "lastName": "Ray"
    }
]

11.2 Update existing user: Now changed logged in user to “grace” in AuditAware class.

Endpoint: http://localhost:8080/users

HTTP Method: PUT

You will get updated data of the user in the response of Postman client.

{
    "id": 2,
    "createdBy": "lucifer",
    "createdTime": "2020-06-27T12:28:49.000+0000",
    "lastModifiedBy": "grace",
    "lastModifiedDate": "2020-06-27T12:34:09.384+0000",
    "firstName": "Atul",
    "lastName": "Sharma"
}

12. Conclusion

In this article, we have learned how to audit insert, update of an entity. But this method won’t audit the delete functionality of an entity. As Spring Data JPA provides an abstraction layer over JPA but both JPA and Spring Data JPA fails to audit delete functionality.

13. References

  1. How to get user details in Spring Security
  2. Spring Data JPA
  3. Spring Auditing

Similar Posts

About the Author

Manish Fartiyal
Hi!, I'm Manish Fartiyal, a full-stack web application developer. I love Java and other open-source technology, currently working at one of the top MNC in India. Read all published posts by Manish Fartiyal.