为具有 Spring 的 @transactional 的方法覆盖事务传播级别
Overriding transaction propagation levels for methods having Spring's @transactional
我的代码库中有多个方法,这些方法用 Spring's @transactional
注释,具有不同的传播级别(让我们忽略选择传播级别背后的想法)。例子-
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; }
}
现在我有一个新用例,我想在单个事务中执行所有这些操作(仅针对此特定用例,不修改现有行为),覆盖任何带注释的传播级别。例子-
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();
}
}
Spring是否提供了一些实现此目的的方法?这不可能吗?
- 我能想到的一种方法是拆分原来的方法
@Transactional(Propagation.NOT_SUPPORTED)
public void A() { A_actual(); }
// Call A_actual from A and newOperation
public void A_actual() { do_something; }
但这可能不像这个例子那么简单(可能有很多这样的方法,这样做可能无法扩展)。而且看起来也不是很干净
- 此外,用例也可能看起来违反直觉,但无论如何让我们将其排除在本问题的范围之外。
我相信唯一的选择是通过 BeanPostProcessor
替换 TransactionInterceptor
,smth。喜欢:
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();
}
}
但是,我同意@jim-garrison 重构您的 spring beans 的建议。
UPD.
But you favour refactoring the beans instead of following this approach. So for the sake of completeness, can you please mention any issues/shortcomings with this
好吧,spring 框架中有很多 things/concepts/ideas 实现时没有 understanding/anticipating 后果(我相信目标是使框架对没有经验的开发人员具有吸引力),并且@Transactional
注释就是其中之一。让我们考虑以下代码:
@Transactional(Propagation.REQUIRED)
public void doSomething() {
do_something;
}
问题是:为什么我们要在那个方法上面加上 @Transactional(Propagation.REQUIRED)
注解?有人可能会说……像这样:
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.
那是错误的:
@Transactional
注释用不相关的信息破坏堆栈跟踪
- 如果出现异常,它将其加入的现有事务标记为 rollback-only - 在调用方无法补偿该异常之后
在大多数情况下,开发人员不应使用 @Transactional(Propagation.REQUIRED)
- 从技术上讲,我们只需要一个关于事务状态的简单断言。
使用@Transactional(Propagation.REQUIRES_NEW)
危害更大:
- 在现有事务的情况下,它从连接池中获取另一个 JDBC-connection,因此您开始每个线程获得 2+ 个连接 - 这会影响性能调整
- 您需要仔细观察您正在使用的数据 - 数据损坏和 self-locks 是使用
@Transactional(Propagation.REQUIRES_NEW)
的结果,因为现在您在同一个线程中有相同数据的两个化身
在大多数情况下,@Transactional(Propagation.REQUIRES_NEW)
表明您的代码需要重构。
所以,关于 @Transactional
注释的一般想法是不要仅仅因为我们可以就在任何地方使用它,而你的问题实际上证实了这个想法:你未能将 3 种方法结合在一起只是因为开发人员有一些关于应该如何执行这些方法的假设。
我的代码库中有多个方法,这些方法用 Spring's @transactional
注释,具有不同的传播级别(让我们忽略选择传播级别背后的想法)。例子-
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; }
}
现在我有一个新用例,我想在单个事务中执行所有这些操作(仅针对此特定用例,不修改现有行为),覆盖任何带注释的传播级别。例子-
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();
}
}
Spring是否提供了一些实现此目的的方法?这不可能吗?
- 我能想到的一种方法是拆分原来的方法
但这可能不像这个例子那么简单(可能有很多这样的方法,这样做可能无法扩展)。而且看起来也不是很干净@Transactional(Propagation.NOT_SUPPORTED) public void A() { A_actual(); } // Call A_actual from A and newOperation public void A_actual() { do_something; }
- 此外,用例也可能看起来违反直觉,但无论如何让我们将其排除在本问题的范围之外。
我相信唯一的选择是通过 BeanPostProcessor
替换 TransactionInterceptor
,smth。喜欢:
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();
}
}
但是,我同意@jim-garrison 重构您的 spring beans 的建议。
UPD.
But you favour refactoring the beans instead of following this approach. So for the sake of completeness, can you please mention any issues/shortcomings with this
好吧,spring 框架中有很多 things/concepts/ideas 实现时没有 understanding/anticipating 后果(我相信目标是使框架对没有经验的开发人员具有吸引力),并且@Transactional
注释就是其中之一。让我们考虑以下代码:
@Transactional(Propagation.REQUIRED)
public void doSomething() {
do_something;
}
问题是:为什么我们要在那个方法上面加上 @Transactional(Propagation.REQUIRED)
注解?有人可能会说……像这样:
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.
那是错误的:
@Transactional
注释用不相关的信息破坏堆栈跟踪- 如果出现异常,它将其加入的现有事务标记为 rollback-only - 在调用方无法补偿该异常之后
在大多数情况下,开发人员不应使用 @Transactional(Propagation.REQUIRED)
- 从技术上讲,我们只需要一个关于事务状态的简单断言。
使用@Transactional(Propagation.REQUIRES_NEW)
危害更大:
- 在现有事务的情况下,它从连接池中获取另一个 JDBC-connection,因此您开始每个线程获得 2+ 个连接 - 这会影响性能调整
- 您需要仔细观察您正在使用的数据 - 数据损坏和 self-locks 是使用
@Transactional(Propagation.REQUIRES_NEW)
的结果,因为现在您在同一个线程中有相同数据的两个化身
在大多数情况下,@Transactional(Propagation.REQUIRES_NEW)
表明您的代码需要重构。
所以,关于 @Transactional
注释的一般想法是不要仅仅因为我们可以就在任何地方使用它,而你的问题实际上证实了这个想法:你未能将 3 种方法结合在一起只是因为开发人员有一些关于应该如何执行这些方法的假设。