Auditing with JPA, Hibernate, and Spring Data JPA

Auditing is an essential feature in many applications to keep track of changes made to the data. Spring Data JPA provides built-in support for auditing, making it easy to log who created or modified an entity and when it was done. This tutorial will guide you through setting up auditing in a Spring Boot 3.2 application using JPA and Hibernate.

Prerequisites

  • JDK 17 or later
  • Maven or Gradle
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Set Up a Spring Boot Project

1.1 Create a New Spring Boot Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Data JPA
  • H2 Database

Download and unzip the project, then open it in your IDE.

1.2 Configure application.properties

Set up the application properties for your project to connect to the H2 database. This file is located in the src/main/resources directory.

# src/main/resources/application.properties

# H2 Database configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.datasource.platform=h2

# JPA configuration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

# H2 Console configuration
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

Explanation:

  • Configures the connection to the H2 in-memory database.
  • Enables SQL logging.
  • Sets up JPA to update the database schema automatically.
  • Enables the H2 console for easy database management.

Step 2: Enable JPA Auditing

2.1 Create an Auditing Configuration Class

Create a configuration class to enable JPA auditing.

package com.example.demo.config;

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

@Configuration
@EnableJpaAuditing
public class AuditingConfig {
}

Explanation:

  • @Configuration: Marks the class as a configuration class.
  • @EnableJpaAuditing: Enables JPA Auditing in the Spring Boot application.

Step 3: Create Auditable Entities

3.1 Create an Auditable Base Entity

Create a base entity class that will hold the auditing fields.

package com.example.demo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime;
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 {

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedBy
    private String lastModifiedBy;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    // Getters and setters
    public String getCreatedBy() {
        return createdBy;
    }

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

    public LocalDateTime getCreatedDate() {
        return createdDate;
    }

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

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }

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

    public LocalDateTime getLastModifiedDate() {
        return lastModifiedDate;
    }

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

Explanation:

  • @MappedSuperclass: Indicates that this class is not an entity itself but its properties should be inherited by other entities.
  • @EntityListeners(AuditingEntityListener.class): Registers the auditing entity listener to handle auditing events.
  • @CreatedBy, @CreatedDate, @LastModifiedBy, @LastModifiedDate: Fields annotated for auditing.

3.2 Create the Student Entity

Create an entity class to represent a student in the database, extending the Auditable base class.

package com.example.demo.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Student extends Auditable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
    private int age;

    // Getters and setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Explanation:

  • Student entity class extends Auditable to inherit auditing fields and behavior.

Step 4: Create the Repository Interface

4.1 Create the StudentRepository

Create a repository interface to perform CRUD operations on the Student entity.

package com.example.demo.repository;

import com.example.demo.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
}

Explanation:

  • @Repository: Indicates that the interface is a Spring Data repository.
  • JpaRepository<Student, Long>: Provides CRUD operations for the Student entity.

Step 5: Create Service and Controller Layers

5.1 Create the StudentService

Create a service class to handle business logic related to students.

package com.example.demo.service;

import com.example.demo.entity.Student;
import com.example.demo.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    public List<Student> getAllStudents() {
        return studentRepository.findAll();
    }

    public Optional<Student> getStudentById(Long id) {
        return studentRepository.findById(id);
    }

    public Student createStudent(Student student) {
        return studentRepository.save(student);
    }

    public void deleteStudent(Long id) {
        studentRepository.deleteById(id);
    }
}

Explanation:

  • @Service: Marks the class as a service component in Spring.
  • StudentRepository: Injected to interact with the database.

5.2 Create the StudentController

Create a REST controller to expose endpoints for interacting with students.

package com.example.demo.controller;

import com.example.demo.entity.Student;
import com.example.demo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping
    public List<Student> getAllStudents() {
        return studentService.getAllStudents();
    }

    @GetMapping("/{id}")
    public Optional<Student> getStudentById(@PathVariable Long id) {
        return studentService.getStudentById(id);
    }

    @PostMapping
    public Student createStudent(@RequestBody Student student) {
        return studentService.createStudent(student);
    }

    @DeleteMapping("/{id}")
    public void deleteStudent(@PathVariable Long id) {
        studentService.deleteStudent(id);
    }
}

Explanation:

  • @RestController: Marks the class as a REST controller.
  • @RequestMapping("/students"): Maps the controller to /students endpoint.
  • @GetMapping, @PostMapping, @DeleteMapping: Maps HTTP GET, POST, and DELETE requests respectively.
  • @RequestBody: Binds the HTTP request body to the Student parameter.
  • @PathVariable: Binds the URI template variable to the method parameter.

Step 6: Running and Testing the Application

6.1 Run the Application

Run the Spring Boot application using your IDE or the command line:

./mvnw spring-boot:run

6.2 Access the H2 Console

You can access the H2 console to inspect the database contents at http://localhost:8080/h2-console. Use the following JDBC URL to connect:

  • JDBC URL: jdbc:h2:mem:testdb
  • Username: sa
  • Password: password

6.3 Test the Endpoints

Use a tool like Postman or your browser to test the endpoints.

Create a Student

  • URL: http://localhost:8080/students
  • Method: POST
  • Body:
    
    
    

{ "name": "John Doe", "email": "john.doe@example.com", "age": 20 }


#### Get All Students

- URL: `http://localhost:8080/students`
- Method: GET

#### Get a Student by ID

- URL: `http://localhost:8080/students/{id}`
- Method: GET

#### Delete a Student

- URL: `http://localhost:8080/students/{id}`
- Method: DELETE

## Conclusion

In this tutorial, you have learned how to implement auditing in a Spring Boot 3.2 application using JPA and Hibernate. We covered:
- Setting up a Spring Boot project with Spring Data JPA and H2.
- Enabling JPA auditing.
- Defining auditable entities.
- Creating service and controller layers.
- Running and testing the application using REST endpoints.

By following these steps, you can effectively track changes to your entities in your Spring Boot applications using Spring Data JPA's auditing capabilities.

Comments