I need to ignore the following @Transactional
annotation during my integration tests.
@Service
public class MyClass {
@Transactional(propagation = Propagation.NEVER)
public void doSomething() {
// do something that once in production can not be inside a transaction (reasons are omitted)
}
}
The problem is that all my tests are executed inside a transaction that is rolled back by default. How could I ignore the @Transactional(propagation = Propagation.NEVER)
annotation for this method when it is running in the scope of a test (@ActiveProfiles("test")
) allowing it to be executed inside a transaction?
First of all, you need to exclude your current @EnableTransactionManagement
annotation to be active in your test
profile. You can do this by isolating the @EnableTransactionManagement
annotation to a separate configuration class which excludes the profile test
so it only gets activated whenever the test
profile is not active.
@EnableTransactionManagement(mode=AdviceMode.PROXY)
@Profile("!test")
public class TransactionManagementConfig {}
With that in place, we can start building up the custom transaction management configuration for your test profile. First we define an annotation that will be used to activate custom transaction management (javadoc comments stripped for compactness of the example, see EnableTransactionManagement
javadoc for details).
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CustomTransactionManagementConfigurationSelector.class)
public @interface EnableCustomTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
Then we need an import selector. Note, that since you're using AdviceMode.PROXY
, i skipped implementing the ASPECTJ
part, but that should be done analogously in order to use AspectJ based transaction management.
public class CustomTransactionManagementConfigurationSelector extends
AdviceModeImportSelector<EnableCustomTransactionManagement> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {
AutoProxyRegistrar.class.getName(),
CustomTransactionAttributeSourceConfig.class.getName()
};
case ASPECTJ:
default:
return null;
}
}
}
And finally the part where you will be able to override the transaction attributes. This one subclasses ProxyTransactionManagementConfiguration
for AdviceMode.PROXY
, you would need an analogous implementation based on AspectJTransactionManagementConfiguration
for AdviceMode.ASPECTJ
. Feel free to implement your own logic, whether be it a constant override of whatever attributes the original AnnotationTransactionAttributeSource
would determine as in my example, or going to greater lengths by introducing and handling your own custom annotation for this purpose.
@Configuration
public class CustomTransactionAttributeSourceConfig
extends ProxyTransactionManagementConfiguration {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = AnnotationAttributes
.fromMap(importMetadata.getAnnotationAttributes(
EnableCustomTransactionManagement.class.getName(),
false));
Assert.notNull(this.enableTx,
"@EnableCustomTransactionManagement is not present on importing class "
+ importMetadata.getClassName());
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Override
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource() {
private static final long serialVersionUID = 1L;
@Override
protected TransactionAttribute findTransactionAttribute(
Class<?> clazz) {
TransactionAttribute transactionAttribute =
super.findTransactionAttribute(clazz);
if (transactionAttribute != null) {
// implement whatever logic to override transaction attributes
// extracted from @Transactional annotation
transactionAttribute = new DefaultTransactionAttribute(
TransactionAttribute.PROPAGATION_REQUIRED);
}
return transactionAttribute;
}
@Override
protected TransactionAttribute findTransactionAttribute(
Method method) {
TransactionAttribute transactionAttribute =
super.findTransactionAttribute(method);
if (transactionAttribute != null) {
// implement whatever logic to override transaction attributes
// extracted from @Transactional annotation
transactionAttribute = new DefaultTransactionAttribute(
TransactionAttribute.PROPAGATION_REQUIRED);
}
return transactionAttribute;
}
};
}
}
Finally, you'll need to enable the custom transaction management configuration with a configuration class tied to the test
profile.
@EnableCustomTransactionManagement(mode=AdviceMode.PROXY)
@Profile("test")
public class TransactionManagementTestConfig {}
I hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With