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.
<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:
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.
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.
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.
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
.
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.
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
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.
Download Source Code: spring-data-jpa-auditing-using-auditoraware.zip