1. 概述
通常我们在应用中会需要对于数据库的操作进行日志的审计,以保存当时的数据操作者及时间等相关信息,为今后的数据变化跟踪作铺垫,在需要时可以追踪到具体修改数据的人。这个时候可以使用Envers或spring数据jpa审计,我的这篇博客记录了一些常用的JPA,Hibernate及SpringDataJPA 的审计方案。如果由于某些原因你不能使用Envers和Spring JPA的审计,那么还有一个方法,就是实现与hibernate事件监听器,并和spring事务资源同步进行绑定,然后在事务提交的时候进行处理。
2. 事件监听
首先,从事件监听器开始。捕获所有插入,更新和删除操作。如下所示:
1 |
|
请注意AuditedEntity
- 它是一个自定义标记注解(retention = runtime,target = type),注解在实体类上,用来标识这个实体是需要被审计的。
3. 事务绑定
同时,在捕获到这些事件后,将事件信息填充到 AuditLogServiceData
中去,AuditLogServiceData
是我们自己定义的一个数据结构,如下所示:
1 | public class AuditLogServiceData { |
除了存储事件之外,我们还需要存储正在执行操作的用户。为了得到它,我们需要提供一个方法参数级注解来指定一个参数。我的案例中的注解被称为AuditLogActor
(retention = runtime,type = parameter)。
这里模拟了 OpenSessionInViewInterceptor
的实现方式,将这些事件及操作者信息绑定到当前的事务上,这样就可以在进行事务提交的时候获取到这些事件及操作者的相关信息,并填充到审计字段上。
4. 事件拦截与审计处理
现在剩下的是处理事件的代码。我们希望在提交当前事务之前执行此操作。如果事务在提交时失败,则审计条目插入也将失败。我们用一点AOP来做到这一点:
1 |
|
我们采用实现 TransactionSynchronizationAdapter
的方式来拦截事务的处理过程,在事务提交前对当前的操作进行审计,代码中的注解已经说明得很清楚了。
5. 实际的调用
如同第2节撰述,首先我们的 FooBar
实体需要添加 AuditedEntity
注解,然后在我们进行保存的地方添加我们的 @AuditLogActor
来标记我们当前的操作者信息,如下所示:
1 |
|
6. 总结
总结一下:整个过程其实就是监听hibernate事件,将所有insert,update和delete事件存储为spring事务同步资源,然后通过 AOP 注册 Spring 事务 “callback”,该事务在每个事务提交之前调用,处理所有事件并插入相应的审核日志条目。
当然,这个方案还是比较烦琐和有一定的局限性的,比如需要在各个事务操作的地方传递 @AuditLogActor
,所以如第1章所述,我们还是应该尽量使用Envers或spring数据jpa审计,只有在这两个方案不适用时才考虑使用本文介绍的案例。