📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (176K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
Table of Contents
- What we’ll build
- Tools and Technologies Used
- Creating and Importing a Project
- Packaging Structure
- The pom.xml File
- Configuring MySQL Database
- Create Generic Auditable Class with Spring Data Annotations @CreatedBy, @CreatedDate, @LastModifiedBy, and @LastModifiedDate
- Create a JPA Entity which extends Auditable Class - User.java
- Auditing Author Using AuditorAware and Spring Security
- Enable JPA Auditing by Using @EnableJpaAuditing
- Create Spring Data JPA Repository - UserRepository.java
- Creating UserController(Contains REST APIs)
- Running the Application
- Source code on GitHub
1. What we’ll build
2. Tools and Technologies Used
- Spring Boot - 3
- JDK - 17 or later
- Spring Framework - 6+
- Maven - 3.2+
- IDE - Eclipse or Spring Tool Suite (STS)
- MYSQL
3. Create and Set up Spring Boot Project
- Generate: Maven Project
- Java Version: 17 (Default)
- Spring Boot: 30.4
- Group: net.guides.springboot
- Artifact: springboot2-jpa-auditing
- Name: springboot2-jpa-auditing
- Package Name: net.guides.springboot.springboot2jpaauditing
- Packaging: jar (This is the default value)
- Dependencies: Web, JPA, MySQL, DevTools
4. Packaging Structure
5. The pom.xml File
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.guides.springboot</groupId>
<artifactId>springboot2-jpa-auditing</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot2-jpa-auditing</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
</properties>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
6. Configuring MySQL Database
spring.datasource.url = jdbc:mysql://localhost:3306/users_database?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
7. Create Generic Auditable Class with Spring Data Annotations @CreatedBy, @CreatedDate, @LastModifiedBy, and @LastModifiedDate
package net.guides.springboot.springboot2jpaauditing.audit;
import static jakarta.persistence.TemporalType.TIMESTAMP;
import java.util.Date;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Temporal;
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;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
@CreatedBy
protected U createdBy;
@CreatedDate
@Temporal(TIMESTAMP)
protected Date createdDate;
@LastModifiedBy
protected U lastModifiedBy;
@LastModifiedDate
@Temporal(TIMESTAMP)
protected Date lastModifiedDate;
public U getCreatedBy() {
return createdBy;
}
public void setCreatedBy(U createdBy) {
this.createdBy = createdBy;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public U getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(U lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public Date getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(Date lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}
8. Create a JPA Entity which extends Auditable Class - User.java
package net.guides.springboot.springboot2jpaauditing.model;
import jakarta.persistence.*;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import net.guides.springboot.springboot2jpaauditing.audit.Auditable;
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User extends Auditable<String> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(name = "email_address", nullable = false)
private String emailId;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
}
- @CreatedDate - Declares a field as the one representing the date the entity containing the field was created.
- @LastModifiedDate - Declares a field as the one representing the date the entity containing the field was recently modified.
- @CreatedBy- Declares a field as the one representing the principal that created the entity containing the field.
- @LastModifiedBy - Declares a field as the one representing the principal that recently modified the entity containing the field.
Using the AuditingEntityListener Class With @EntityListeners
9. Auditing Author Using AuditorAware and Spring Security
package net.guides.springboot.springboot2jpaauditing.audit;
import java.util.Optional;
import org.springframework.data.domain.AuditorAware;
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of("Ramesh");
// Use below commented code when will use Spring Security.
}
}
// ------------------ Use below code for spring security --------------------------
/*class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}*/
10. Enable JPA Auditing by Using @EnableJpaAuditing
package net.guides.springboot.springboot2jpaauditing;
import org.springframework.boot.SpringApplication;
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;
import net.guides.springboot.springboot2jpaauditing.audit.AuditorAwareImpl;
@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class Springboot2JpaAuditingApplication {
@Bean
public AuditorAware<String> auditorAware() {
return new AuditorAwareImpl();
}
public static void main(String[] args) {
SpringApplication.run(Springboot2JpaAuditingApplication.class, args);
}
}
11. Create Spring Data JPA Repository - UserRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.companyname.springbootcrudrest.model.User;
@Repository
public interface UserRepository extends JpaRepository<User, Long>{
}
12. Creating UserController(Contains REST APIs)
package net.guides.springboot.springboot2jpaauditing.controller;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.guides.springboot.springboot2jpaauditing.exception.ResourceNotFoundException;
import net.guides.springboot.springboot2jpaauditing.model.User;
import net.guides.springboot.springboot2jpaauditing.repository.UserRepository;
@RestController
@RequestMapping("/api/v1")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll();
}
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(
@PathVariable(value = "id") Long userId) throws ResourceNotFoundException {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found :: " + userId));
return ResponseEntity.ok().body(user);
}
@PostMapping("/users")
public User createUser(@Valid @RequestBody User user) {
return userRepository.save(user);
}
@PutMapping("/users/{id}")
public ResponseEntity<User> updateUser(
@PathVariable(value = "id") Long userId,
@Valid @RequestBody User userDetails) throws ResourceNotFoundException {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found :: " + userId));
user.setEmailId(userDetails.getEmailId());
user.setLastName(userDetails.getLastName());
user.setFirstName(userDetails.getFirstName());
user.setLastModifiedDate(new Date());
final User updatedUser = userRepository.save(user);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/users/{id}")
public Map<String, Boolean> deleteUser(
@PathVariable(value = "id") Long userId) throws ResourceNotFoundException {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found :: " + userId));
userRepository.delete(user);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return response;
}
}
- @RequestMapping("/api/v1") - annotation declares that the url for all the apis in this controller will start with /api/v1
- @RestController - annotation is a combination of Spring’s @Controller and @ResponseBody annotations.
- @GetMapping("/users") - annotation is a short form of @RequestMapping(value="/users", method=RequestMethod.GET).
- @GetMapping("/users/{id}") - annotation is a short form of @RequestMapping(value="/users/{id}", method=RequestMethod.GET).
- @PostMapping("/users") - annotation is a short form of @RequestMapping(value="/users", method=RequestMethod.POST).
- @PutMapping("/users/{id}") - annotation is a short form of @RequestMapping(value="/users/{id}", method=RequestMethod.PUT).
- @DeleteMapping("/user/{id}") - annotation is a short form of @RequestMapping(value="/users/{id}", method=RequestMethod.DELETE).
- @PathVariable - annotation is used to bind a path variable with a method parameter.
13. Running the Application with a Demo
1. Create User REST API
1. Get all Users REST API
14. Source code on GitHub
Read 25+ Spring Boot Articles with Source Code on GitHub on Spring Boot Tutorial
when i send request to update, i did not get the value of created date, created by
ReplyDeletewhile using spring security
ReplyDeleteHi everybody,
ReplyDeleteExcellent! Tuto. I did the exercise and everything excellent. But I have a question...
Spring does not generate a historical table with @Audit ?
Regards.
nice tutorial very useful can u explain annotations in depth.
ReplyDeletethanks
i want one user history table also then what i have to do can u explain me
ReplyDeleteHi, Good tutorial, I have a question, It is tracking when the record is created, may be it will works when it updates. But how it tracks when the record is deleted as the record it self deleted? Can you explain?
ReplyDeleteYou’re manually setting the last modified date in your controller before persisting which defeats the purpose of using the auditing capability. I came here to see an example of that working but looks like this solution just avoids it
ReplyDeleteCreate a Date object in Auditable class - protected Date lastModifiedDate = new Date(). This solves your problem by manually setting modified date.
Deletenice tutorial, but will it also audit changes in a child property? I mean if A Personal class has an Address and I update some property of Address will it also audit that change? Thanks
ReplyDeleteWhen I start my spring boot application, it gets stuck at -
ReplyDeleteHHH000490: Using JtaPlatform implementation: [org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform]
Please help!