1. 概述
在 ORM 的世界里,数据库审计(Auditing)指的是对持久化实体相关的事件进行追踪和记录,也可以理解为实体版本管理。这个概念借鉴了 SQL 中的触发器机制,主要关注实体的插入(insert)、更新(update)和删除(delete)操作。审计的好处类似于代码版本控制,可以帮我们追踪变更历史、排查问题、满足合规性需求。
本文将介绍三种实现审计的方式:
- 使用标准 JPA 生命周期回调
- 使用 Hibernate Envers(Hibernate 提供的审计模块)
- 使用 Spring Data JPA 内置的审计支持
我们以 Bar
和 Foo
两个实体为例,其关系结构如下:
2. 使用 JPA 实现审计
虽然 JPA 本身没有专门的审计 API,但我们可以通过实体生命周期回调(Lifecycle Callbacks)来实现类似功能。
2.1. @PrePersist、*@PreUpdate* 和 @PreRemove
在 JPA 实体类中,我们可以通过注解标记回调方法,使其在特定生命周期事件中被调用。常用的三个注解是:
@Entity
public class Bar {
@PrePersist
public void onPrePersist() { ... }
@PreUpdate
public void onPreUpdate() { ... }
@PreRemove
public void onPreRemove() { ... }
}
⚠️ 注意事项:
- 回调方法必须返回
void
,且不能有参数 - 方法名和访问修饰符可以随意,但不能是
static
📌 一个小提醒:@Version
注解主要用于乐观锁,和审计没有直接关系。
2.2. 实现回调方法
虽然回调机制看起来方便,但 JPA 2 规范(JSR 317)明确指出:
生命周期回调方法不应该调用 EntityManager 或 Query 操作,也不应该访问其他实体或修改关系。
因此,我们只能修改实体的非关系状态。为了实现审计,我们可以手动在实体中添加两个字段:operation
和 timestamp
,记录操作类型和时间戳:
@Entity
public class Bar {
//...
@Column(name = "operation")
private String operation;
@Column(name = "timestamp")
private long timestamp;
// standard setters and getters for the new properties
@PrePersist
public void onPrePersist() {
audit("INSERT");
}
@PreUpdate
public void onPreUpdate() {
audit("UPDATE");
}
@PreRemove
public void onPreRemove() {
audit("DELETE");
}
private void audit(String operation) {
setOperation(operation);
setTimestamp((new Date()).getTime());
}
}
✅ 如果多个实体都需要审计,可以使用 @EntityListeners
将审计逻辑集中管理:
@EntityListeners(AuditListener.class)
@Entity
public class Bar { ... }
public class AuditListener {
@PrePersist
@PreUpdate
@PreRemove
private void beforeAnyOperation(Object object) { ... }
}
3. 使用 Hibernate Envers 实现审计
Hibernate 提供了 Envers 模块,用于对持久化实体进行审计和版本控制。相比手动实现,它更强大、更灵活。
3.1. 引入 Envers
首先添加依赖:
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-envers</artifactId>
<version>6.4.4.Final</version>
</dependency>
然后在实体上添加 @Audited
注解:
@Entity
@Audited
public class Bar { ... }
如果实体之间有关联关系,比如 Bar
和 Foo
是一对多关系,要么在 Foo
上也加 @Audited
,要么在 Bar
的关系字段上加 @NotAudited
:
@OneToMany(mappedBy = "bar")
@NotAudited
private Set<Foo> fooSet;
3.2. 审计表的生成
Envers 会自动为我们生成审计表,有以下几种方式:
✅ 自动建表(开发环境推荐):
- 设置
hibernate.hbm2ddl.auto
为create
、create-drop
或update
❌ 不推荐用于生产环境
举个例子,Bar
会被生成 bar_AUD
表,包含所有字段,外加两个字段:
REV
:修订号,外键指向REVINFO
REVTYPE
:操作类型(0=新增,1=更新,2=删除)
此外,还会生成 REVINFO
表,记录每次修订的时间戳。
3.3. 配置 Envers
Envers 的配置和其他 Hibernate 属性一样。例如,修改审计表后缀:
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"org.hibernate.envers.audit_table_suffix", "_AUDIT_LOG");
sessionFactory.setHibernateProperties(hibernateProperties);
更多配置项可参考 Envers 官方文档。
3.4. 查询历史数据
通过 AuditReader
可以查询历史版本:
AuditReader reader = AuditReaderFactory.get(session);
查询特定修订版本的实体:
AuditQuery query = reader.createQuery()
.forEntitiesAtRevision(Bar.class, 2)
查询某个实体的所有历史版本:
AuditQuery query = reader.createQuery()
.forRevisionsOfEntity(Bar.class, true, true);
排序和过滤:
query.addOrder(AuditEntity.revisionNumber().desc());
4. 使用 Spring Data JPA 实现审计
Spring Data JPA 是 JPA 的增强层,提供了更简洁的接口抽象和内置审计支持。
4.1. 启用 JPA 审计
在配置类上添加注解:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories
@EnableJpaAuditing
public class PersistenceConfig { ... }
4.2. 添加 Spring 的实体监听器
使用 Spring 提供的 AuditingEntityListener
来监听实体变更:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar { ... }
4.3. 记录创建和修改时间
通过 @CreatedDate
和 @LastModifiedDate
自动填充时间字段:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar {
//...
@Column(name = "created_date", nullable = false, updatable = false)
@CreatedDate
private long createdDate;
@Column(name = "modified_date")
@LastModifiedDate
private long modifiedDate;
//...
}
📌 建议将这些字段抽到一个 @MappedSuperclass
基类中复用。
4.4. 记录操作人(Spring Security 集成)
如果项目使用了 Spring Security,可以通过 @CreatedBy
和 @LastModifiedBy
自动记录操作人:
@Column(name = "created_by")
@CreatedBy
private String createdBy;
@Column(name = "modified_by")
@LastModifiedBy
private String modifiedBy;
这些字段会自动从 SecurityContext
中获取当前用户信息。
如果需要自定义逻辑,可以实现 AuditorAware<T>
接口:
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public String getCurrentAuditor() {
// your custom logic
}
}
然后在配置类中启用:
@EnableJpaAuditing(auditorAwareRef="auditorProvider")
public class PersistenceConfig {
@Bean
AuditorAware<String> auditorProvider() {
return new AuditorAwareImpl();
}
}
5. 总结
本文介绍了三种审计实现方式:
✅ JPA 原生方式:使用生命周期回调,适合轻量级场景,但限制较多,删除操作无法记录。
✅ Hibernate Envers:功能强大,支持完整的增删改审计,适合复杂业务场景。
✅ Spring Data JPA:使用注解简化审计逻辑,支持 Spring Security 集成,但同样无法审计删除操作。
📌 实际项目中,推荐使用 Hibernate Envers 或 Spring Data JPA,避免手动维护审计字段。
完整代码示例可参考 GitHub 仓库。