Spring Data JPA Auditing

JPA Sep 20, 2020

Spring Data provides a great support to keep track of the persistence layer changes. By using auditing, we can store or log the information about the changes on the entity such as who created or changed the entity and when the change is made.

We can make use of the annotations like @CreatedBy, @CreatedDate, @LastModifiedDate, @LastModifiedBy annotation on the entity fields to instruct the Spring JPA to transparently fill these fields. We can use the annotations as follows:

@Entity
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long title;
    
    @CreatedBy
    private Long createdBy;

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedBy
    private Long lastModifiedBy;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
    
    // getters and setter..
}

Auditing feature is generally needed in most of the entities, so it is a better approach to create and abstract class Auditable class that contains the auditing fields and extend the abstract class by the entities that need auditing. By this way we will avoid duplicating the same fields in all entities.

Creating Abstract Auditable Class

We can create an Abstract class to contain the audit related fields as follows:

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Auditable {

    @CreatedBy
    @Column(columnDefinition = "bigint default 1", updatable = false)
    protected Long createdBy;

    @CreatedDate
    @Column(columnDefinition = "timestamp default '2020-04-10 20:47:05.967394'", updatable = false)
    protected LocalDateTime createdDate;

    @LastModifiedBy
    @Column(columnDefinition = "bigint default 1")
    protected Long lastModifiedBy;

    @LastModifiedDate
    @Column(columnDefinition = "timestamp default '2020-04-10 20:47:05.967394'")
    protected LocalDateTime lastModifiedDate;

    public Long getCreatedBy() {
        return createdBy;
    }

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

    public LocalDateTime getCreatedDate() {
        return createdDate;
    }

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

    public Long getLastModifiedBy() {
        return lastModifiedBy;
    }

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

    public LocalDateTime getLastModifiedDate() {
        return lastModifiedDate;
    }

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

@MappedSuperclass annotation is used to specify that the class itself is not an entity but its attributes can be mapped in the same way as an entity, however this mappings will apply only to its subclasses. So each class inherits the abstract Auditable class will contain these attributes.

@EntityListeners annotation is used to configure AuditingEntityListener which contains the @PrePersist and @PreUpdate methods in order to capture auditing information

Enable Auditing Feature

In order to enable the auditing feature in Spring we need to use @EnableJpaAuditing annotation.

@SpringBootApplication
@EnableJpaAuditing
public class BackendApplication {
	public static void main(String[] args) {
		SpringApplication.run(BackendApplication.class, args);
	}
}

Provide Auditor

createdDate and lastModifiedDate fields are filled according to the current time. Besides, createdBy and lastModifiedBy annotations needs a way to get the user who is performing the action. In order to provide this information we need to implement the AuditorAware interface.

@Component
public class AuditAwareImpl implements AuditorAware <Long> {

    @Override
    public Optional <Long> getCurrentAuditor() {
        ApplicationUser principal = (ApplicationUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return Optional.of(principal.getId());
    }
}

We added the implementation of getCurrentAuditor method because it is invoked to retrieve the user who is performing the operation.

Extend the Auditable Class in Entities

Now we can extend the Auditable class in the entities that we want to use auditing. For example:

@Entity
@Data
public class Category extends Auditable {

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

    // ..
}

And the auditing fields will be filled automatically:

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.