Spring Data JPA Auditing
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: