Spring和Hibernate的自定义审核日志

如果您需要对所有数据库操作进行自动审核 ,并且正在使用Hibernate…,则应使用Enversspring data jpa auditing 。 但是,如果由于某些原因您不能使用Envers,则可以使用休眠事件侦听器和spring事务同步来实现类似的功能。

首先,从事件监听器开始。 您应该捕获所有插入,更新和删除操作。 但是有一点棘手的问题–如果出于某种原因需要刷新会话,则无法直接使用传递给事件侦听器的会话执行该逻辑。 以我为例,我必须获取一些数据,然后休眠开始向我抛出异常(“ id为null”)。 多个来源确认您不应在事件侦听器中与数据库进行交互。 因此,您应该存储事件以供以后处理。 您可以将侦听器注册为spring bean ,如下所示

@Component
public class AuditLogEventListener
        implements PostUpdateEventListener, PostInsertEventListener, PostDeleteEventListener {

    @Override
    public void onPostDelete(PostDeleteEvent event) {
        AuditedEntity audited = event.getEntity().getClass().getAnnotation(AuditedEntity.class);
        if (audited != null) {
            AuditLogServiceData.getHibernateEvents().add(event);
        }
    }

    @Override
    public void onPostInsert(PostInsertEvent event) {
        AuditedEntity audited = event.getEntity().getClass().getAnnotation(AuditedEntity.class);
        if (audited != null) {
            AuditLogServiceData.getHibernateEvents().add(event);
        }
    }

    @Override
    public void onPostUpdate(PostUpdateEvent event) {
        AuditedEntity audited = event.getEntity().getClass().getAnnotation(AuditedEntity.class);
        if (audited != null) {
            AuditLogServiceData.getHibernateEvents().add(event);
        }
    }

    @Override
    public 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 {

    @Autowired
    private 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 ++;
        }
    }

    @Override
    public 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) { .. }

总结一下:休眠事件监听器将所有插入,更新和删除事件存储为spring事务同步资源。 一个方面用spring注册一个事务“回调”,在提交每个事务之前立即调用它。 在那里处理所有事件,并插入相应的审核日志条目。

这是非常基本的审核日志,可能在收集处理方面有问题,并且当然不能涵盖所有用例。 但这比手动审核日志处理要好得多,并且在许多系统中,审核日志是强制性功能。

翻译自: https://www.javacodegeeks.com/2016/07/custom-audit-log-spring-hibernate.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值