Skip to content

Hibnerate Envers With JPA#

Hibnerate Envers With JPA#

  • In this section we will try to apply auditing with hibernate envers for our Spring Data JPA project.

Dependencies#

  • Let's assume that you have a JPA project and now you want to apply hibernate envers for auditing data. So you just need to apply the dependencies as below to your project.
pom.xml
1
2
3
4
5
6
7
8
9
...

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-envers</artifactId>
        <version>5.4.29.Final</version>
    </dependency>

...
  • Note: the version of hibernate-envers will depend on the version of the hibernate-core in your JPA dependencies. For example, we are using JPA with version as below.
pom.xml
1
2
3
4
5
6
7
8
9
...

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-data-jpa</artifactId>  
    <version>2.4.4</version>  
</dependency>

...
  • Then if we check the hibernate-core of this JPA version, we can see it is using 5.4.29.Final version. So we need to choose the hibernate-envers with version 5.4.29.Final also.

 #zoom

  • When we add hibernate-envers dependency into our spring boot project, it also provide for us some configurations that we can put into our application.yml or application.properties. All configurations are showed as in the table below.
Property name Default value Description
org.hibernate.envers.audit_table_prefix String that will be prepended to the name of an audited entity to create the name of the entity, that will hold audit information.
org.hibernate.envers.audit_table_suffix _AUD String that will be appended to the name of an audited entity to create the name of the entity, that will hold audit information. If you audit an entity with a table name Person, in the default setting Envers will generate a Person_AUD table to store historical data.
org.hibernate.envers.revision_field_name REV Name of a field in the audit entity that will hold the revision number.
org.hibernate.envers.revision_type_field_name REVTYPE Name of a field in the audit entity that will hold the type of the revision (currently, this can be: add, mod, del).
org.hibernate.envers.revision_on_collection_change true Should a revision be generated when a not-owned relation field changes (this can be either a collection in a one-to-many relation, or the field using "mappedBy" attribute in a one-to-one relation).
org.hibernate.envers.do_not_audit_optimistic_locking_field true When true, properties to be used for optimistic locking, annotated with @Version, will be automatically not audited (their history won't be stored; it normally doesn't make sense to store it).
org.hibernate.envers.store_data_at_delete false Should the entity data be stored in the revision when the entity is deleted (instead of only storing the id and all other properties as null). This is not normally needed, as the data is present in the last-but-one revision. Sometimes, however, it is easier and more efficient to access it in the last revision (then the data that the entity contained before deletion is stored twice).
org.hibernate.envers.default_schema null (same as normal tables) The default schema name that should be used for audit tables. Can be overriden using the @AuditTable(schema="...") annotation. If not present, the schema will be the same as the schema of the normal tables.
org.hibernate.envers.default_catalog null (same as normal tables) The default catalog name that should be used for audit tables. Can be overriden using the @AuditTable(catalog="...") annotation. If not present, the catalog will be the same as the catalog of the normal tables.

Applying Hibernate Envers#

  • We can use annotation @Audited to audit data of entities and fields.
    • If we put the @Audited annotation on entity class, so all properties of that entity will be audited.
    • When we put @Audited annotation on a field of an entity class so this field will be audited only.
  • If we don't want to audit a specific field in an entity class that we put @Audited. We can use annotation@NotAudited on this specific one. Let's see the example below.
CustomerEntity.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.springboot.data.hibernate.envers.app.entity;

import org.hibernate.annotations.Type;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Audited
@Entity
@Table(name = "customers")
public class CustomerEntity {

    @Id
    @GeneratedValue
    @Type(type="uuid-char")
    private UUID id;
    private String fullName;
    @Column(unique = true)
    private String email;
    private String address;
    private String phone;
    @Enumerated(EnumType.STRING)
    private Gender gender;
    private Date dob;

    @NotAudited
    @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<OrderEntity> orders = new ArrayList<>();

        ...
        //getter, setter
OrderEntity.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.springboot.data.hibernate.envers.app.entity;

import org.hibernate.annotations.Type;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Audited
@Entity
@Table(name = "orders")
public class OrderEntity {

    @Id
    @GeneratedValue
    @Type(type="uuid-char")
    private UUID id;

    private String orderName;

    private LocalDateTime createdDate;

    private LocalDateTime lastUpdatedDate;

    @Enumerated(value = EnumType.STRING)
    private OrderStatus orderStatus;

    @NotAudited
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id")
    private CustomerEntity customer;

    @NotAudited
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<ItemEntity> items = new ArrayList<>();

        ...
        //getter, setter
ItemEntity.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.springboot.data.hibernate.envers.app.entity;

import org.hibernate.annotations.Type;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;

import javax.persistence.*;
import java.util.UUID;

@Audited
@Entity
@Table(name = "items")
public class ItemEntity {

    @Id
    @GeneratedValue
    @Type(type="uuid-char")
    private UUID id;

    private String itemName;

    private Long quantity;

    private Float price;

    @NotAudited
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private OrderEntity order;

        ...
        //getter, setter
  • As you can see, in examples above we will try to audit all fields in entities with @Audited annotation but we will exclude fields which are used for entities relationships with @NotAudited annotation.

Testing#

  • Now, Let's start our Spring Boot JPA project and go to database to check tables. We see tables with suffix "aud" are generated automatically.

 #zoom

  • Then, let's use postman and create customer data. Then let's go audit tables and you can see the audit data has been saved successfully.

 #zoom

  • customers_aud table
id rev revtype address dob email full_name gender phone
6ecc2e24-bd38-496b-bda1-7af3ce420a6c 1 0 Binh Duong Province 1995-10-10 07:00:00 abc3@gmail.com Nguyen Minh Duc M 0123456789
  • orders_aud table
id rev revtype created_date last_updated_date order_name order_status
1b962ecd-6994-428c-9c07-d54b93c569c4 1 0 2022-10-29 10:39:39 2022-10-29 10:39:39 PC CREATED
  • items_aud table
id rev revtype item_name price quantity
0ea9a57c-86a4-4508-ab78-0d264e3848f4 1 0 Monitor 100.0 1
  • Now, let's use postman to update data for customer and check the customer_aud again. We will see there is a new record in audit table for tracking changes of the customer.

 #zoom

  • customers_aud table
id rev revtype address dob email full_name gender phone
6ecc2e24-bd38-496b-bda1-7af3ce420a6c 1 0 Binh Duong Province 1995-10-10 07:00:00 abc3@gmail.com Nguyen Minh Duc M 0123456789
6ecc2e24-bd38-496b-bda1-7af3ce420a6c 2 1 Ho Chi Minh City 1995-10-10 07:00:00 abc5@gmail.com Nguyen Minh Duc M 0123456789
  • For other tables they are still have one record in audit table because there are no change in their data.

  • orders_aud table

id rev revtype created_date last_updated_date order_name order_status
1b962ecd-6994-428c-9c07-d54b93c569c4 1 0 2022-10-29 10:39:39 2022-10-29 10:39:39 PC CREATED
  • items_aud table
id rev revtype item_name price quantity
0ea9a57c-86a4-4508-ab78-0d264e3848f4 1 0 Monitor 100.0 1

See Also#