如何在 Hibernate 4 应用程序中全面拦截插入/更新/删除数据库事务

gen*_* b. 6 spring hibernate jpa transactions

我创建了几个关于特定问题的类似线程,但让我总结一下我在全球范围内面临的问题:

目标

需要拦截使用 Spring 4.1.5、Hibernate 4.3.8 编写的应用程序中的所有插入/更新/删除数据库事务。该应用程序使用 Hibernate 的所有用法:

  • 通过会话基于对象,例如sessionFactory.getCurrentSession().saveOrUpdate(obj);
  • HQL执行更新,例如Query q = "update Obj ..."; q.executeUpdate();
  • 标准API,例如Criteria q = sessionFactory.getCurentSession().createCriteria(..); q.list();

可能的方法

  • 拦截器:实现拦截器。

public class MyInterceptor extends EmptyInterceptor {..}

重写 (1) 事务级方法或 (2) 特定操作方法。

交易级别:

@Override
    public void afterTransactionBegin(Transaction tx) {                 
        super.afterTransactionBegin(tx);            
    }
Run Code Online (Sandbox Code Playgroud)

具体行动级别

@Override
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
    return super.onSave(entity, id, state, propertyNames, types);
}

@Override
public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
    super.onDelete(entity, id, state, propertyNames, types);
}
Run Code Online (Sandbox Code Playgroud)
  • EventListener:实现 aPreUpdateEventListenerPreInsertEventListener注册它。

    @Component
    public class HibernateSaveUpdateEventListener implements PreUpdateEventListener, PreInsertEventListener  {
        @Override
        public boolean onPreInsert(PreInsertEvent arg0) {
            //...
        }
        @Override
        public boolean onPreUpdate(PreUpdateEvent arg0) {
          //...
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

问题

  • EventListener或方法都不会Interceptor拦截HQLexecuteUpdate()。但我们的应用程序中有很多这样的例子。其他一些用户已经证实:

https://forum.hibernate.org/viewtopic.php?f=1&t=1012054

https://coderanch.com/t/669945/java/Hibernate-interceptor-working-hibernate-query

似乎问题是EventListener/Interceptor只能与Session. 但HQLexecuteUpdate是在Query对象上,而不是Session.

  • 无法使用afterTransactionBegin(Transaction tx)其中任何一个的事务级拦截器方法。原因是,我无法区分只读选择事务和写入事务。tx不告诉我我是否处于readOnly=false交易中readOnly=true。因此,覆盖将对所有afterTransactionBegin事务触发,我需要将其限制为非只读事务(插入、更新、删除)。

@Transactional(readOnly = false) @Transactional(readOnly = true)

那么对于同时使用对象操作和HQL操作的Hibernate应用程序来说,有没有一种解决方案可以全面拦截Insert/Update/Delete呢?

gen*_* b. 1

有一种简单的方法可以使用Spring 级别的事务侦听器检查只读/非只读事务,这将包括外部 SQL/HQL(非实体)。在 Spring 级别,而不是 Hibernate,有一个名为的方法TransactionSynchronizationManager.isCurrentTransactionReadOnly()告诉我们是否处于我们正在拦截的只读事务中。可以从以下实现中调用它,TransactionSynchronization该实现使用切入点来拦截所有@Before @TransactionalSpring 处理。

@Component
@Aspect
public class TransactionSynchronizationAspect implements TransactionSynchronization {   
    
    @Before("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void registerTransactionSynchronization() {
        boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
        
        if (!isReadOnly) {
            // ... logic goes here...
        }

        // Continue
        TransactionSynchronizationManager.registerSynchronization(this);

    }

    @Override
    public void afterCompletion(int status) {
        // No custom code here
    }
}
Run Code Online (Sandbox Code Playgroud)