Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 事务启动时的侦听器_Java_Spring_Hibernate_Spring Transactions_Hibernate Session - Fatal编程技术网

Java 事务启动时的侦听器

Java 事务启动时的侦听器,java,spring,hibernate,spring-transactions,hibernate-session,Java,Spring,Hibernate,Spring Transactions,Hibernate Session,我正在寻找一个干净的解决方案,为事务启动提供一个侦听器。这意味着我希望侦听器是spring上下文中的一个bean(组件),它将在新事务启动时从TransactionPlatformManager或Hibernate会话或类似的地方接收事务启动事件 一些事情: @Component class TransactionListener implements ?? { @Autowired private Something x; public void onTransac

我正在寻找一个干净的解决方案,为事务启动提供一个侦听器。这意味着我希望侦听器是spring上下文中的一个bean(组件),它将在新事务启动时从TransactionPlatformManager或Hibernate会话或类似的地方接收事务启动事件

一些事情:

@Component
class TransactionListener implements ?? {

    @Autowired
    private Something x;

    public void onTransactionBegin(...) {
        x.doSomething()
    }

}
具体来说,我正在缓解一个系统范围的问题,我需要在事务启动时设置一个本地线程,以便在处理hibernate实体以检索信息时进一步访问该本地线程


我调查了资料来源,没有发现这样的聆听者是可以做到的。我找到的唯一解决方案是将HibernateTransactionManager及其doBegin()方法子类化,我觉得这不是特别好。

Spring在其中有一些事务回调,但是正如您正确地注意到的,事务启动没有回调,这是我的错误

据我所知,Spring不会让您知道事务何时开始,尽管这可能因不同的实现而有所不同
PlatformTransactionManager
。如果你想参与Spring交易,我相信你只剩下

  • 子类化事务管理器并调用一些回调
  • 使用SpringAOP为
    @Transactional
    创建一个建议(显然,这仅在使用注释时有效)

  • 如果您使用的是Hibernate,您可能会幸运地使用中的
    afterTransactionBegin

    到目前为止,这对我很有效

    @Aspect
    @Component
    public class StartTransactionInterceptor {
    
        @Pointcut("target(org.springframework.transaction.PlatformTransactionManager)")
        public void isPlatformTransactionManager() {
        }
    
        @Pointcut("execution(org.springframework.transaction.TransactionStatus getTransaction("
                + "org.springframework.transaction.TransactionDefinition)))")
        public void getsTransaction() {
        }
    
        @Around("isPlatformTransactionManager() && getsTransaction()")
        public Object registerSynchronization(ProceedingJoinPoint joinPoint) throws Throwable {
            TransactionStatus value = (TransactionStatus)joinPoint.proceed();
            if (value.isNewTransaction()) {
                // send some application event to others who are interested
            }
            return value;
        }
    }
    
    或者,您可以使用Spring的
    SimpleTransactionScope
    并为事务作用域定义bean的作用域。当其他人在事务中调用下面的
    DealWithStuffPerTx.addMoreTuff(Object)
    时,bean将被延迟实例化

    @Configuration
    public class TransactionScopeConfig implements BeanFactoryPostProcessor {
    
        public static final String NAME = "tx";
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            beanFactory.registerScope(NAME, new SimpleTransactionScope());
        }
    }
    
    @Component
    @Scope(value = TransactionScopeConfig.NAME, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class DealWithStuffPerTx extends TransactionSynchronizationAdapter {
    
        public void addMoreStuff(Object stuff) {
        }
    
        @Override
        public void afterCommit() {
            // deal with stuff
        }
    
        @PostConstruct
        public void init() {
            TransactionSynchronizationManager.registerSynchronization(this);
        }
    }
    

    我遇到了一个类似的问题,我希望在事务启动时立即记录Oracle会话id,以便调查我们遇到的一些问题

    最后我发现,由于Spring使用了
    PlatformTransactionManager
    ,您可以通过定制来访问所有信息

    首先要做的是确定您正在使用的实现。在我们的例子中,它是在
    @Configuration
    类中声明的一个简单的
    JpaTransactionManager
    ,因此非常简单

    完成此操作后,请注意,您可以在此类上启用调试或跟踪日志记录,如果您的目标是调试问题,则该类已经提供了大量事务状态信息

    如果这还不够,可以很容易地将其子类化并替换上一个。然后只需覆盖您想要拦截的方法,如
    doBegin()
    prepareSynchronization()

    例如,请参见我的实现:

    @Slf4j
    public class LoggingJpaTransactionManager extends JpaTransactionManager {
        @Autowired
        private EntityManager entityManager;
    
        LoggingJpaTransactionManager(EntityManagerFactory emf) {
            super(emf);
        }
    
        @Override
        protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
            super.prepareSynchronization(status, definition);
            if (status.isNewTransaction() && log.isInfoEnabled()) {
                Query query = entityManager.createNativeQuery("select sys_context('USERENV','SID') from dual");
                Object sessionId = query.getSingleResult();
                log.info("Started a new transaction on session id {}", sessionId);
                TransactionSynchronizationManager.registerSynchronization(…);
            }
        }
    }
    

    注意:我选择覆盖
    prepareSynchronization()
    而不是
    doBegin()
    ,因为它允许使用
    TransactionSynchronizationManager
    ,我认为它在收到提交/回滚事件的通知时更干净。无论如何,在
    doBegin()
    之后会立即调用此方法。

    将通知除事务开始之外的所有事件。最简单的方法可能是为
    PlatformTransactionManager
    创建一个包装器,并在3个方法调用中激发这些事件。但是你为什么要注册这个?你想做什么?你提到解决问题,但这样做似乎有点奇怪。我面临的问题与此无关,它是由我们的大型多模块体系结构引起的。如果我可以选择对架构做出决策,我会用不同的方式来做。但是在这个遗留体系结构中,如果没有沉浸式重写,这是不可能的,这就是我需要它的原因。方面解决方案看起来不错,但不幸的是,它只适用于JDK代理,而不适用于CGLIB类代理,因为
    AbstractPlatformTransactionManager
    中有许多
    final
    方法,在您尝试启动事务时立即导致NPE。据我所知,第二种方法无法全局拦截事务,它只适用于调用
    addMoreStuff()
    的代码。