简体   繁体   English

覆盖具有 Spring 的 @transactional 的方法的事务传播级别

[英]Overriding transaction propagation levels for methods having Spring's @transactional

I have multiple methods in my codebase annotated with Spring's @transactional with different propgation levels (lets ignore the idea behind choosing the propagation levels).我的代码库中有多种方法,用Spring 的@transactional注释并具有不同的传播级别(让我们忽略选择传播级别背后的想法)。 Example -例子 -

public class X {
    @Transactional(Propagation.NOT_SUPPORTED)
    public void A() { do_something; }

    @Transactional(Propagation.REQUIRED)
    public void B() { do_something; }

    @Transactional(Propagation.REQUIRES_NEW)
    public void C() { do_something; }
}

Now I have a new use case where I want to perform all these operations in a single transaction ( for this specific use case only, without modifying existing behavior ), overriding any annotated propagation levels.现在我有一个新的用例,我想在单个事务中执行所有这些操作(仅针对此特定用例,不修改现有行为),覆盖任何带注释的传播级别。 Example -例子 -

public class Y {
    private X x;

    // Stores application's global state
    private GlobalState globalState;

    @Transactional
    public void newOperation() {
         // Set current operation as the new operation in the global state, 
         // in case this info might be required somewhere
         globalState.setCurrentOperation("newOperation");

         // For this new operation A, B, C should be performed in the current 
         // transaction regardless of the propagation level defined on them
         x.A();
         x.B();
         x.C();
    }
}

Does Spring provide some way to achieve this ? Spring是否提供了一些方法来实现这一点? Is this not possible ?这不可能吗?

  • One way I could think of is to split the original methods我能想到的一种方法是拆分原始方法
    @Transactional(Propagation.NOT_SUPPORTED) public void A() { A_actual(); } // Call A_actual from A and newOperation public void A_actual() { do_something; }
    But this might not be as simple to do as this example (there can be a lot of such methods and doing this might not scale).但这可能不像这个例子那么简单(可能有很多这样的方法,这样做可能无法扩展)。 Also it does not look much clean.而且看起来也不是很干净。
  • Also the use case might also appear counter intuitive, but anyway let's keep that out of scope of this question.此外,用例也可能显得反直觉,但无论如何让我们将其排除在这个问题的范围之外。

I do believe the only option is to replace TransactionInterceptor via BeanPostProcessor , smth.我相信唯一的选择是通过BeanPostProcessor替换TransactionInterceptor ,smth。 like:喜欢:

public class TransactionInterceptorExt extends TransactionInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // here some logic determining how to proceed invocation
        return super.invoke(invocation);
    }

}
public class TransactionInterceptorPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor, BeanFactoryAware {

    @Setter
    private BeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.addBeanPostProcessor(this);
    }

    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        if (bean instanceof TransactionInterceptor) {
            TransactionInterceptor interceptor = (TransactionInterceptor) bean;
            TransactionInterceptor result = new TransactionInterceptorExt();
            result.setTransactionAttributeSource(interceptor.getTransactionAttributeSource());
            result.setTransactionManager(interceptor.getTransactionManager());
            result.setBeanFactory(beanFactory);
            return result;
        }
        return bean;
    }

}
@Configuration
public class CustomTransactionConfiguration {

    @Bean
    //@ConditionalOnBean(TransactionInterceptor.class)
    public static BeanFactoryPostProcessor transactionInterceptorPostProcessor() {
        return new TransactionInterceptorPostProcessor();
    }

}

However, I would agree with @jim-garrison suggestion to refactor your spring beans.但是,我同意@jim-garrison 重构你的spring bean 的建议。

UPD. UPD。

But you favour refactoring the beans instead of following this approach.但是您更喜欢重构 bean 而不是遵循这种方法。 So for the sake of completeness, can you please mention any issues/shortcomings with this因此,为了完整起见,您能否提及任何问题/缺点

Well, there are a plenty of things/concepts/ideas in spring framework which were implemented without understanding/anticipating consequences (I believe the goal was to make framework attractive to unexperienced developers), and @Transactional annotation is one of such things.好吧,spring 框架中有很多东西/概念/想法是在没有理解/预期后果的情况下实现的(我相信目标是让框架对没有经验的开发人员有吸引力), @Transactional注释就是其中之一。 Let's consider the following code:让我们考虑以下代码:

    @Transactional(Propagation.REQUIRED)
    public void doSomething() { 
        do_something; 
    }

The question is: why do we put @Transactional(Propagation.REQUIRED) annotation above that method?问题是:为什么我们要在该方法上方放置@Transactional(Propagation.REQUIRED)注释? Someone might say smth.有人可能会说smth。 like this:像这样:

that method modifies multiple rows/tables in DB and we would like to avoid inconsistencies in our DB, moreover Propagation.REQUIRED does not hurt anything, because according to the contract it either starts new transaction or joins to the exisiting one.该方法修改了数据库中的多个行/表,我们希望避免数据库中的不一致,而且Propagation.REQUIRED不会伤害任何东西,因为根据合同,它要么开始新事务,要么加入现有事务。

and that would be wrong:那将是错误的:

  • @Transactional annotation poisons stacktraces with irrelevant information @Transactional注释用不相关的信息毒化堆栈跟踪
  • in case of exception it marks existing transaction it joined to as rollback-only - after that caller side has no option to compensate that exception如果出现异常,它会将其加入的现有事务标记为仅回滚-在调用方没有选项来补偿该异常之后

In the most cases developers should not use @Transactional(Propagation.REQUIRED) - technically we just need a simple assertion about transaction status.在大多数情况下,开发人员不应该使用@Transactional(Propagation.REQUIRED) - 从技术上讲,我们只需要一个关于事务状态的简单断言。

Using @Transactional(Propagation.REQUIRES_NEW) is even more harmful:使用@Transactional(Propagation.REQUIRES_NEW)更加有害:

  • in case of existing transaction it acquires another one JDBC-connection from connection pool, and hence you start getting 2+ connections per thread - this hurts performance sizing在现有事务的情况下,它会从连接池中获取另一个 JDBC 连接,因此您开始每个线程获得 2 个以上的连接 - 这会损害性能大小
  • you need to carefully watch for data you are working with - data corruptions and self-locks are the consequences of using @Transactional(Propagation.REQUIRES_NEW) , cause now you have two incarnations of the same data within the same thread您需要仔细观察您正在使用的数据 - 数据损坏和自锁是使用@Transactional(Propagation.REQUIRES_NEW)的后果,因为现在您在同一个线程中有相同数据的两个化身

In the most cases @Transactional(Propagation.REQUIRES_NEW) is an indicator that you code requires refactoring.在大多数情况下, @Transactional(Propagation.REQUIRES_NEW)指示您的代码需要重构。

So, the general idea about @Transactional annotation is do not use it everywhere just because we can, and your question actually confirms this idea: you have failed to tie up 3 methods together just because developer had some assumptions about how those methods should being executed.所以,关于@Transactional注释的一般想法是不要仅仅因为我们可以在任何地方使用它,而你的问题实际上证实了这个想法:你没有将 3 个方法捆绑在一起只是因为开发人员对这些方法应该如何执行有一些假设.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM