introduction
Data auditing is a crucial feature in enterprise application development. The so-called data audit refers to tracking and recording data creation, modification and other operations to facilitate subsequent data analysis, problem tracking and security audit. Spring Data JPA provides powerful auditing functions, and can automatically record the entity creation time, last modification time, create person and modify person through simple annotation configuration. This article will explore the audit functions of Spring Data JPA in depth, and focus on the implementation principles and usage methods of @CreatedDate and @LastModifiedDate annotations, helping developers build a robust data audit system.
1. Overview of Spring Data JPA audit functions
Spring Data JPA's auditing capabilities are implemented through entity lifecycle events and AOP sections. It automatically populates audit fields when entities are persisted and updated, thus avoiding the tedious work of manually setting these values.
1.1 Core Audit Annotations
Spring Data JPA provides four core audit notes:
-
@CreatedDate
: Mark entity creation time field -
@LastModifiedDate
: Mark the last modified time field of the entity -
@CreatedBy
: Tag entity creator field -
@LastModifiedBy
: Mark entity last modified field
These annotations are located inIn the package, it is a common audit annotation for Spring Data, not limited to JPA usage.
1.2 How the audit function works
The audit function of Spring Data JPA mainly works together through the following components:
-
AuditingEntityListener
: JPA entity listener, responsible for capturing the entity's life cycle events -
AuditingHandler
: Processing the filling logic of audit information -
DateTimeProvider
: Provides an interface for the current time -
AuditorAware
: The interface that provides current user information
When the audit function is enabled, whenever the entity is created or updated,AuditingEntityListener
The corresponding event will be captured and calledAuditingHandler
Fill in fields marked with audit annotations.
2. Basic configuration
To use the audit function of Spring Data JPA, the necessary configuration is first required.
2.1 Enable JPA audit function
In Spring Boot app,@EnableJpaAuditing
Annotation Enable JPA audit function:
package ; import ; import ; /** * JPA audit function configuration class */ @Configuration @EnableJpaAuditing // Enable JPA audit functionpublic class JpaAuditingConfig { // You can configure audit-related beans here}
2.2 Create an audit base class
Usually, we will create a base class containing audit fields, so that the entity that needs auditing inherits this base class:
package ; import ; import ; import ; import ; import ; import ; import ; /** * The underlying entity class containing the audit field */ @MappedSuperclass @EntityListeners() // Register entity listenerpublic abstract class AuditableEntity { @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; // Getter and Setter methods public LocalDateTime getCreatedDate() { return createdDate; } public void setCreatedDate(LocalDateTime createdDate) { = createdDate; } public LocalDateTime getLastModifiedDate() { return lastModifiedDate; } public void setLastModifiedDate(LocalDateTime lastModifiedDate) { = lastModifiedDate; } }
In the above code:
-
@MappedSuperclass
The annotation indicates that this is a mapped superclass whose fields will be mapped to the table of the subclass. -
@EntityListeners()
Registered JPA entity listener to capture the lifecycle events of an entity -
@CreatedDate
Tagged entity creation time field -
@LastModifiedDate
Tagged the entity's last modified time field -
updatable = false
make surecreatedDate
Fields will not be modified when updated
Implement time audit
Time audit is the most basic audit function, involving@CreatedDate
and@LastModifiedDate
Use of annotations.
3.1 Using audit base class
Create a business entity class and inherit the audit base class:
package ; import .*; import ; /** * Product Entity */ @Entity @Table(name = "tb_product") public class Product extends AuditableEntity { @Id @GeneratedValue(strategy = ) private Long id; @Column(name = "name", nullable = false) private String name; @Column(name = "description") private String description; @Column(name = "price", precision = 10, scale = 2) private BigDecimal price; @Column(name = "stock") private Integer stock; //Getter and Setter methods are omitted}
inheritAuditableEntity
back,Product
Entities will automatically owncreatedDate
andlastModifiedDate
Fields, these fields are automatically populated when entities are created and updated.
3.2 Audit implementation without using base class
If you do not want to use inheritance for some reason, you can also use audit annotations directly in the entity class:
package ; import ; import ; import ; import .*; import ; /** * Order entity class */ @Entity @Table(name = "tb_order") @EntityListeners() // Register the listener directly on the entity classpublic class Order { @Id @GeneratedValue(strategy = ) private Long id; @Column(name = "order_number", nullable = false, unique = true) private String orderNumber; @Column(name = "customer_id") private Long customerId; @Column(name = "total_amount", precision = 10, scale = 2) private BigDecimal totalAmount; @Column(name = "status") private String status; @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; //Getter and Setter methods are omitted}
3.3 Custom date and time provider
If you need to provide custom date and time, you can achieveDateTimeProvider
interface:
package ; import ; import ; import ; import ; import ; import ; /** * Custom date and time provider */ @Component public class CustomDateTimeProvider implements DateTimeProvider { @Override public Optional<TemporalAccessor> getNow() { // Use the current time of a specific time zone return ((("Asia/Shanghai"))); } }
Then register this provider in the configuration class:
package ; import ; import ; import ; import ; import ; /** * JPA audit function configuration class */ @Configuration @EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider") // Provider of specified date and timepublic class JpaAuditingConfig { @Bean public DateTimeProvider dateTimeProvider() { return new CustomDateTimeProvider(); } }
IV. Implement user audit
In addition to time auditing, user auditing can also be implemented, that is, recording users who create and modify entities.
4.1 Extended audit base class
Extend the audit base class and add user audit fields:
package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; /** * Base entity class containing the complete audit field */ @MappedSuperclass @EntityListeners() public abstract class FullAuditableEntity { @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; @CreatedBy @Column(name = "created_by", updatable = false) private String createdBy; @LastModifiedBy @Column(name = "last_modified_by") private String lastModifiedBy; //Getter and Setter methods are omitted}
4.2 Implement AuditorAware interface
To use@CreatedBy
and@LastModifiedBy
Annotation, need to be implementedAuditorAware
Interface, providing current user information:
package ; import ; import ; import ; import ; import ; /** * Current user provider */ @Component public class SpringSecurityAuditorAware implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { // Get the current user from Spring Security context Authentication authentication = ().getAuthentication(); if (authentication == null || !()) { return ("anonymousUser"); } return (()); } }
Then register this provider in the configuration class:
package ; import ; import ; import ; import ; import ; import ; import ; /** * JPA audit function configuration class */ @Configuration @EnableJpaAuditing( dateTimeProviderRef = "dateTimeProvider", auditorAwareRef = "auditorAware" // Designate user provider) public class JpaAuditingConfig { @Bean public DateTimeProvider dateTimeProvider() { return new CustomDateTimeProvider(); } @Bean public AuditorAware<String> auditorAware() { return new SpringSecurityAuditorAware(); } }
4.3 Using a complete audit entity
Create a business entity and inherit the complete audit base class:
package ; import .*; import ; /** * Order entity class */ @Entity @Table(name = "tb_order") public class Order extends FullAuditableEntity { @Id @GeneratedValue(strategy = ) private Long id; @Column(name = "order_number", nullable = false, unique = true) private String orderNumber; @Column(name = "customer_id") private Long customerId; @Column(name = "total_amount", precision = 10, scale = 2) private BigDecimal totalAmount; @Column(name = "status") private String status; //Getter and Setter methods are omitted}
5. Practical application scenarios
Spring Data JPA's audit function has a wide range of application scenarios in actual development.
5.1 Data version control
Combining version control fields, optimizing locking and data version tracking:
package ; import .*; import ; import ; import ; import ; /** * Document entity class */ @Entity @Table(name = "tb_document") @EntityListeners() public class Document { @Id @GeneratedValue(strategy = ) private Long id; @Column(name = "title") private String title; @Column(name = "content", columnDefinition = "TEXT") private String content; @Version // Version control field @Column(name = "version") private Long version; @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; //Getter and Setter methods are omitted}
5.2 Audit logging
Using entity listeners, more detailed audit logging is achieved:
package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; /** * Product Audit Listener */ @Component public class ProductAuditListener { @Autowired private AuditLogRepository auditLogRepository; private static final ThreadLocal<Product> originalState = new ThreadLocal<>(); @PrePersist public void prePersist(Product product) { // Operation before creating a new product } @PostPersist public void postPersist(Product product) { // Record product creation log AuditLog log = new AuditLog(); ("Product"); (().toString()); ("CREATE"); (()); ("Created product: " + ()); (log); } @PreUpdate public void preUpdate(Product product) { // Save the original state of the product Product original = new Product(); // Copy the product properties to the original (original); } @PostUpdate public void postUpdate(Product product) { // Get the original state Product original = (); // Record product update log AuditLog log = new AuditLog(); ("Product"); (().toString()); ("UPDATE"); (()); // Build change information StringBuilder changes = new StringBuilder(); if (!().equals(())) { ("Name changed from '") .append(()) .append("' to '") .append(()) .append("'. "); } // Check for other fields changes... (()); (log); // Clean up ThreadLocal (); } }
To enable this listener, you need to register on the Product entity:
@Entity @Table(name = "tb_product") @EntityListeners({, }) public class Product extends AuditableEntity { // Entity content}
5.3 Multi-tenant audit
In a multi-tenant system, the audit function is used to achieve tenant data isolation:
package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; /** * Multi-tenant audit base class */ @MappedSuperclass @EntityListeners() public abstract class TenantAuditableEntity { @Column(name = "tenant_id", nullable = false, updatable = false) private String tenantId; @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; @CreatedBy @Column(name = "created_by", updatable = false) private String createdBy; @LastModifiedBy @Column(name = "last_modified_by") private String lastModifiedBy; //Getter and Setter methods are omitted}
Using multi-tenant audit entities:
package ; import .*; /** * Customer Entity Class */ @Entity @Table(name = "tb_customer") public class Customer extends TenantAuditableEntity { @Id @GeneratedValue(strategy = ) private Long id; @Column(name = "name") private String name; @Column(name = "email") private String email; @Column(name = "phone") private String phone; //Getter and Setter methods are omitted}
6. Advanced skills
Spring Data JPA auditing features also have some advanced usages that can meet more complex audit needs.
6.1 Conditional Audit
Sometimes we only want audits under certain conditions. It can be implemented through a custom entity listener:
package ; import ; import ; import ; import ; import ; import ; import ; /** * Conditional Audit Listener */ public class ConditionalAuditListener { @PrePersist public void touchForCreate(Object target) { // Only audit users in active status if (target instanceof User) { User user = (User) target; if (()) { setCreatedDate(user); } } } @PreUpdate public void touchForUpdate(Object target) { // Only audit users in active status if (target instanceof User) { User user = (User) target; if (()) { setLastModifiedDate(user); } } } private void setCreatedDate(Object target) { setFieldValue(target, , ()); } private void setLastModifiedDate(Object target) { setFieldValue(target, , ()); } private void setFieldValue(Object target, Class annotation, Object value) { try { for (Field field : ().getDeclaredFields()) { if ((annotation)) { (true); (target, value); } } } catch (IllegalAccessException e) { throw new RuntimeException("Failed to set auditing field", e); } } }
6.2 Custom audit notes
Custom audit annotations can be created to achieve more flexible audit logic:
package ; import ; import ; import ; import ; /** * Custom audit notes: record the historical value of the field */ @Retention() @Target() public @interface TrackChanges { String value() default ""; }
Then implement the corresponding listener to process this annotation:
package ; import ; import ; import ; import ; import ; import ; import ; import ; /** * Field change tracking listener */ public class FieldChangeTrackingListener { @Autowired private FieldChangeLogRepository changeLogRepository; @PreUpdate public void preUpdate(Object entity) { try { // Get the entity ID Long entityId = getEntityId(entity); String entityType = ().getSimpleName(); // Find the fields marked @TrackChanges for (Field field : ().getDeclaredFields()) { TrackChanges annotation = (); if (annotation != null) { (true); // Get the new value of the field Object newValue = (entity); // Get the original entity and field old values from the database Object originalEntity = loadOriginalEntity(entityId, ()); (true); Object oldValue = (originalEntity); // If the value changes, log if (!(oldValue, newValue)) { FieldChangeLog changeLog = new FieldChangeLog(); (entityType); (entityId); (()); (oldValue != null ? () : null); (newValue != null ? () : null); (()); (changeLog); } } } } catch (Exception e) { throw new RuntimeException("Failed to track field changes", e); } } private Long getEntityId(Object entity) throws Exception { // Logic for getting entity ID // ... return null; } private Object loadOriginalEntity(Long entityId, Class<?> entityClass) { //Log the original entity from the database // ... return null; } }
Summarize
Spring Data JPA's auditing capabilities provide a powerful and flexible mechanism for automatically tracking the creation and modification of entities. By using @CreatedDate and @LastModifiedDate annotations, developers can easily implement time audits; combined with @CreatedBy and @LastModifiedBy annotations and AuditorAware interfaces, user audits can also be implemented. These functions greatly simplify the development of audit systems and enable developers to focus on the implementation of business logic.
In actual applications, the audit function can be combined with scenarios such as version control, detailed logging and multi-tenant systems to meet different business needs. More complex and flexible audit logic can also be implemented through custom entity listeners and audit annotations. In short, the audit function of Spring Data JPA is an important part of building robust enterprise-level applications and is of great significance to improving system traceability and security.
This is the end of this article about the implementation of SpringData JPA audit function (@CreatedDate and @LastModifiedDate). For more related SpringData JPA audit content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!