在本文中,我们将讨论如何配置JPA以自动持久保存任何实体的CreatedBy,CreatedDate,LastModifiedBy和LastModifiedDate标注的字段列。我们将创建一个简单的Spring Boot CRUD REST API,并使用spring数据JPA实现审计。
在任何业务应用程序中,审计意味着跟踪和记录我们在持久记录中所做的每一项更改,这意味着跟踪每个插入,更新和删除操作并存储它。审计有助于我们维护历史记录,以后可以帮助我们跟踪用户操作系统的活动。
这样就不需要在每次保存,更新或删除操作上进行手动编写代码,我们完全可以使用第三方库自动执行。Spring Data提供了复杂的支持,可以透明地跟踪创建或更改实体的人员以及发生这种情况的时间点。
首先必须为实体类配备审计元数据,该元数据可以使用注释或通过实现接口来定义。在此示例中,我们将使用审计字段创建一个通用的通用Auditable抽象类,以便任何实体都可以扩展它以使用审计。
直接使用JPA依赖即可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
使用Spring数据注释创建通用可审计类@CreatedBy,@ CreatedDate,@ LastModifiedBy和@LastModifiedDate:
@MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class Auditable<U> { @CreatedBy protected U createdBy; @CreatedDate @Temporal(TIMESTAMP) protected Date createdDate; @LastModifiedBy protected U lastModifiedBy; @LastModifiedDate @Temporal(TIMESTAMP) protected Date lastModifiedDate; public U getCreatedBy() { return createdBy; } public void setCreatedBy(U createdBy) { this.createdBy = createdBy; } public Date getCreatedDate() { return createdDate; } public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; } public U getLastModifiedBy() { return lastModifiedBy; } public void setLastModifiedBy(U lastModifiedBy) { this.lastModifiedBy = lastModifiedBy; } public Date getLastModifiedDate() { return lastModifiedDate; } public void setLastModifiedDate(Date lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; } }
让我们理解重要的JPA审核注释:
@CreatedDate - 声明一个字段,表示创建包含该字段的实体的日期。
@LastModifiedDate - 声明一个字段,表示最近修改了包含该字段的实体的日期。
@CreatedBy - 声明一个字段,表示创建包含该字段的实体的主体。
@LastModifiedBy - 声明一个字段,表示最近修改包含该字段的实体的主体。
使用带有@EntityListeners的AuditingEntityListener类,Spring Data JPA提供了一个JPA实体监听器类 AuditingEntityListener ,它包含回调方法(使用 @PrePersist 和 @PreUpdate 注释),当我们将持久化或更新我们的实体时,它将用于持久化和更新这些属性。
JPA提供了 @EntityListeners 注释来指定回调监听器类,我们用它来注册我们的 AuditingEntityListener 类。
JPA可以使用当前系统时间分析createdDate和lastModifiedDate,但是createdBy和lastModifiedBy字段呢?JPA将如何识别存储在其中的内容?
要告诉JPA当前登录的用户,我们需要提供AuditorAware的实现并覆盖getCurrentAuditor()方法。在getCurrentAuditor()中,我们需要获取当前登录的用户。
提供了一个硬编码用户,但是如果你使用的是Spring Security,则可用它来查找当前登录的用户。
public class AuditorAwareImpl implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { return Optional.of("Ramesh"); // Use below commented code when will use Spring Security. } } // ------------------ Use below code for spring security -------------------------- /*class SpringSecurityAuditorAware implements AuditorAware<User> { public User getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { return null; } return ((MyUserDetails) authentication.getPrincipal()).getUser(); } }*/
现在,我们可以创建一个业务JPA实体继承上面Auditable类:
@Entity @Table(name = "users") @EntityListeners(AuditingEntityListener.class) public class User extends Auditable<String> { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Column(name = "first_name", nullable = false) private String firstName; @Column(name = "last_name", nullable = false) private String lastName; @Column(name = "email_address", nullable = false) private String emailId; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmailId() { return emailId; } public void setEmailId(String emailId) { this.emailId = emailId; } }
指定 @EnableJpaAuditing 来启用JPA审计:
@SpringBootApplication @EnableJpaAuditing(auditorAwareRef = "auditorAware") public class Springboot2JpaAuditingApplication { @Bean public AuditorAware<String> auditorAware() { return new AuditorAwareImpl(); } public static void main(String[] args) { SpringApplication.run(Springboot2JpaAuditingApplication.class, args); } }
现在,我们完成了所有JPA审计的主要设置。
源码连接Github