spring 自定义日志
如果您需要对所有数据库操作进行自动审核 ,并且正在使用Hibernate…,则应使用Envers或spring data jpa auditing 。 但是,如果由于某些原因您不能使用Envers,则可以使用Hibernate事件侦听器和spring事务同步来实现类似的功能。
首先,从事件监听器开始。 您应该捕获所有插入,更新和删除操作。 但是有一点棘手的问题–如果出于任何原因需要刷新会话,则无法使用传递给事件侦听器的会话直接执行该逻辑。 以我为例,我必须获取一些数据,然后Hibernate开始向我抛出异常(“ id为null”)。 多个来源确认您不应在事件侦听器中与数据库进行交互。 因此,您应该存储事件以供以后处理。 您可以将侦听器注册为spring bean ,如下所示 。
@Component
public class AuditLogEventListenerimplements PostUpdateEventListener, PostInsertEventListener, PostDeleteEventListener {@Overridepublic void onPostDelete(PostDeleteEvent event) {AuditedEntity audited = event.getEntity().getClass().getAnnotation(AuditedEntity.class);if (audited != null) {AuditLogServiceData.getHibernateEvents().add(event);}}@Overridepublic void onPostInsert(PostInsertEvent event) {AuditedEntity audited = event.getEntity().getClass().getAnnotation(AuditedEntity.class);if (audited != null) {AuditLogServiceData.getHibernateEvents().add(event);}}@Overridepublic void onPostUpdate(PostUpdateEvent event) {AuditedEntity audited = event.getEntity().getClass().getAnnotation(AuditedEntity.class);if (audited != null) {AuditLogServiceData.getHibernateEvents().add(event);}}@Overridepublic boolean requiresPostCommitHanding(EntityPersister persister) {return true; // Envers sets this to true only if the entity is versioned. So figure out for yourself if that's needed}
}
注意AuditedEntity
–这是一个自定义标记注释(retention = runtime,target = type),您可以将其放置在实体之上。
老实说,我没有完全了解Envers如何进行持久化 ,但是由于我也可以使用spring,因此在我的AuditLogServiceData
类中,我决定使用spring:
/*** {@link AuditLogServiceStores} stores here audit log information It records all * changes to the entities in spring transaction synchronizaton resources, which * are in turn stored as {@link ThreadLocal} variables for each thread. Each thread * /transaction is using own copy of this data.*/
public class AuditLogServiceData {private static final String HIBERNATE_EVENTS = "hibernateEvents";@SuppressWarnings("unchecked")public static List<Object> getHibernateEvents() {if (!TransactionSynchronizationManager.hasResource(HIBERNATE_EVENTS)) {TransactionSynchronizationManager.bindResource(HIBERNATE_EVENTS, new ArrayList<>());}return (List<Object>) TransactionSynchronizationManager.getResource(HIBERNATE_EVENTS);}public static Long getActorId() {return (Long) TransactionSynchronizationManager.getResource(AUDIT_LOG_ACTOR);}public static void setActor(Long value) {if (value != null) {TransactionSynchronizationManager.bindResource(AUDIT_LOG_ACTOR, value);}}
}
除了存储事件之外,我们还需要存储执行操作的用户。 为了做到这一点,我们需要提供一个方法-参数级注释来指定一个参数。 在我的案例中,注释称为AuditLogActor
(保留=运行时,类型=参数)。
现在剩下的将是处理事件的代码。 我们想在提交当前事务之前执行此操作。 如果事务在提交时失败,则审计条目插入也将失败。 我们通过一点AOP来做到这一点:
@Aspect
@Component
class AuditLogStoringAspect extends TransactionSynchronizationAdapter {@Autowiredprivate ApplicationContext ctx; @Before("execution(* *.*(..)) && @annotation(transactional)")public void registerTransactionSyncrhonization(JoinPoint jp, Transactional transactional) {Logger.log(this).debug("Registering audit log tx callback");TransactionSynchronizationManager.registerSynchronization(this);MethodSignature signature = (MethodSignature) jp.getSignature();int paramIdx = 0;for (Parameter param : signature.getMethod().getParameters()) {if (param.isAnnotationPresent(AuditLogActor.class)) {AuditLogServiceData.setActor((Long) jp.getArgs()[paramIdx]);}paramIdx ++;}}@Overridepublic void beforeCommit(boolean readOnly) {Logger.log(this).debug("tx callback invoked. Readonly= " + readOnly);if (readOnly) {return;}for (Object event : AuditLogServiceData.getHibernateEvents()) {// handle events, possibly using instanceof}}
在我的情况下,我不得不注入其他服务,并且spring抱怨相互依赖的bean,所以我改用了applicationContext.getBean(FooBean.class)
。 注意:确保您的方面被spring所捕获–通过自动扫描,或通过xml / java-config显式注册它。
因此,已审核的呼叫将如下所示:
@Transactional
public void saveFoo(FooRequest request, @AuditLogActor Long actorId) { .. }
总结一下:Hibernate事件监听器将所有插入,更新和删除事件存储为Spring事务同步资源。 一个方面用spring注册一个事务“回调”,在提交每个事务之前立即调用它。 在那里处理所有事件,并插入相应的审核日志条目。
这是非常基本的审核日志,可能在收集处理方面存在问题,并且当然不能涵盖所有用例。 但这比手动审核日志处理要好得多,并且在许多系统中,审核日志是强制性功能。
翻译自: https://www.javacodegeeks.com/2016/07/custom-audit-log-spring-hibernate.html
spring 自定义日志