How to Audit JPA Entities Using Envers in a Spring Boot Application

In this tutorial, we will walk through the process of setting up auditing for JPA entities using Envers in a Spring Boot application. We will create an example Spring Boot project, define JPA entities, configure Hibernate Envers, and demonstrate how to retrieve audit information from the database.

In modern applications, tracking data changes is crucial, especially in critical systems handling sensitive information. JPA, a widely used Java Persistence API, works with databases. Hibernate Envers, part of Hibernate ORM, enables auditing for JPA entities, maintaining a historical record for easier tracking and analysis of modifications. Learn how to implement it in a Spring Boot app with this tutorial.

To follow along with this tutorial, you need the following prerequisites:

  1. Basic knowledge of Java, Spring Boot, and JPA.
  2. Java Development Kit (JDK) 8 or higher installed on your system.
  3. Spring Boot CLI or an Integrated Development Environment (IDE) like IntelliJ IDEA or Eclipse.
  4. MySQL or another relational database system installed.

Create a Spring Boot Project

Let’s begin by creating a new Spring Boot project using the Spring Boot Initializr. Open your terminal or command prompt and execute the following command:

spring init --dependencies=web,jpa,mysql --name=SpringBootEnversExample SpringBootEnversExample
cd SpringBootEnversExample

This will create a new Spring Boot project with the required dependencies for web, JPA, and MySQL.

Define JPA Entities

In this tutorial, we will create a simple Book entity to demonstrate auditing using Envers. Open your IDE and create a new Java class Book under the com.example.demo package with the following code:

import javax.persistence.*;

@Entity
@Table(name = "books")
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private String author;

    // Constructors, getters, and setters (omitted for brevity)
}

In this Book entity, we have defined the primary key id, along with title and author fields. These fields will be audited by Hibernate Envers.

Configure Hibernate Envers

To enable auditing with Hibernate Envers, we need to configure it in our Spring Boot application. Create a new configuration class EnversConfiguration under the com.example.demo package with the following code:

import org.hibernate.envers.configuration.EnversSettings;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class EnversConfiguration {

    // No additional configuration needed for this example.
}

By adding @EnableJpaAuditing to the configuration class, we instruct Spring Boot to enable JPA auditing using Envers.

Set Up Database Connection for JPA Entities

Next, we need to configure the database connection for our Spring Boot application. Open the application.properties file located in the src/main/resources directory and update the following properties:

# MySQL properties
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_database_username
spring.datasource.password=your_database_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update

# Envers properties
spring.jpa.properties.org.hibernate.envers.audit_table_suffix=_aud
spring.jpa.properties.org.hibernate.envers.revision_field_name=revision
spring.jpa.properties.org.hibernate.envers.revision_type_field_name=revision_type

Replace your_database_name, your_database_username, and your_database_password with your MySQL database details. The spring.jpa.hibernate.ddl-auto property is set to update, which means Hibernate will automatically create the necessary tables for the Book entity and Envers audit tables.

Implement the Repository for JPA entities

Now, let’s implement the Spring Data JPA repository to perform CRUD operations on the Book entity. Create a new Java interface BookRepository under the com.example.demo package with the following code:

import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book, Long> {

}

This interface extends JpaRepository and provides basic CRUD operations for the Book entity.

Test the Application

Congratulations! You have now implemented auditing for JPA entities using Hibernate Envers in your Spring Boot application. To test the auditing feature, let’s create a few Book instances, save them, update some records, and then retrieve audit information.

In your DemoApplication class, add the following code to test the auditing:

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

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);

        // Get the BookRepository bean
        BookRepository bookRepository = context.getBean(BookRepository.class);

        // Create and save a new book
        Book book1 = new Book();
        book1.setTitle("Book 1");
        book1.setAuthor("Author 1");
        bookRepository.save(book1);

        // Update the book
        Book bookToUpdate = bookRepository.findById(1L).orElse(null);
        if (bookToUpdate != null) {
            bookToUpdate.setTitle("Updated Book 1");
            bookRepository.save(bookToUpdate);
        }

        // Retrieve the audit information for the book with id 1
        Book bookAudit = bookRepository.findAuditById(1L, 1);
        System.out.println("Audit Information for Book with id 1:");
        System.out.println("Title: " + bookAudit.getTitle());
        System.out.println("Author: " + bookAudit.getAuthor());
        System.out.println("Revision Type: " + bookAudit.getRevisionType());
    }
}

In the DemoApplication class, we retrieve the BookRepository bean using the application context. We then create and save a new Book, update it, and finally retrieve the audit information for the book with id 1 using the custom findAuditById method (which we will implement in the next step).

Implement Custom Audit Query

To retrieve the audit information, we need to implement a custom query in the BookRepository. Create a new method in the BookRepository interface as follows:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface BookRepository extends JpaRepository<Book, Long> {

    @Query("SELECT b FROM Book_AUD b WHERE b.id = :id AND b.revision = :revision")
    Book findAuditById(@Param("id") Long id, @Param("revision") int revision);
}

In this custom query, we are using the @Query annotation to specify the JPQL (Java Persistence Query Language) query to retrieve the audit information for a given id and revision.

Run the Application

Now, it’s time to run the Spring Boot application. Open your terminal or command prompt and navigate to the project’s root directory. Execute the following command:

./mvnw spring-boot:run

The application will start, and you will see the audit information for the Book entity with id 1 displayed on the console.

Conclusion

In this tutorial, you learned how to audit JPA entities using Hibernate Envers in a Spring Boot application. Envers allows you to maintain a historical record of changes made to your data, making it easier to track and analyze modifications over time. You implemented auditing for a Book entity and retrieved the audit information for a specific revision. You can now apply this knowledge to add auditing to other entities in your Spring Boot applications, providing a more robust and secure system.

Remember that auditing should be used judiciously, as it can generate a substantial amount of data over time. Make sure to fine-tune the configuration and consider purging old audit records periodically to optimize storage and performance.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment