Introduce resolvable timeout attribute on @Transactional and <tx:method>

Placeholders get resolved in timeoutString, qualifier and labels now.

Closes gh-25052
This commit is contained in:
Juergen Hoeller
2020-05-12 21:55:22 +02:00
parent 273d952ddf
commit dd0d0d51f6
14 changed files with 244 additions and 63 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,6 +69,7 @@ public class TxNamespaceHandlerTests {
assertThat(ptm.begun).as("Should not have any started transactions").isEqualTo(0);
testBean.getName();
assertThat(ptm.lastDefinition.isReadOnly()).isTrue();
assertThat(ptm.lastDefinition.getTimeout()).isEqualTo(5);
assertThat(ptm.begun).as("Should have 1 started transaction").isEqualTo(1);
assertThat(ptm.commits).as("Should have 1 committed transaction").isEqualTo(1);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -140,22 +140,31 @@ public class AnnotationTransactionAttributeSourceTests {
@Test
public void transactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception {
Method interfaceMethod = ITestBean3.class.getMethod("getAge");
Method interfaceMethod2 = ITestBean3.class.getMethod("getName");
Method interfaceMethod2 = ITestBean3.class.getMethod("setAge", int.class);
Method interfaceMethod3 = ITestBean3.class.getMethod("getName");
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
atas.setEmbeddedValueResolver(strVal -> ("${myTimeout}".equals(strVal) ? "5" : strVal));
TransactionAttribute actual = atas.getTransactionAttribute(interfaceMethod, TestBean3.class);
assertThat(actual.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRES_NEW);
assertThat(actual.getIsolationLevel()).isEqualTo(TransactionAttribute.ISOLATION_REPEATABLE_READ);
assertThat(actual.getTimeout()).isEqualTo(5);
assertThat(actual.isReadOnly()).isTrue();
TransactionAttribute actual2 = atas.getTransactionAttribute(interfaceMethod2, TestBean3.class);
assertThat(actual2.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRES_NEW);
assertThat(actual2.getIsolationLevel()).isEqualTo(TransactionAttribute.ISOLATION_REPEATABLE_READ);
assertThat(actual2.getTimeout()).isEqualTo(5);
assertThat(actual2.isReadOnly()).isTrue();
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
assertThat(((RuleBasedTransactionAttribute) actual).getRollbackRules()).isEqualTo(rbta.getRollbackRules());
TransactionAttribute actual2 = atas.getTransactionAttribute(interfaceMethod2, TestBean3.class);
assertThat(actual2.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRED);
TransactionAttribute actual3 = atas.getTransactionAttribute(interfaceMethod3, TestBean3.class);
assertThat(actual3.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRED);
}
@Test
@@ -585,6 +594,9 @@ public class AnnotationTransactionAttributeSourceTests {
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ,
timeoutString = "${myTimeout}", readOnly = true, rollbackFor = Exception.class,
noRollbackFor = IOException.class)
public void setAge(int age) {
this.age = age;
}

View File

@@ -18,6 +18,7 @@ package org.springframework.transaction.annotation;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;
import org.junit.jupiter.api.Test;
@@ -31,13 +32,16 @@ import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.config.TransactionManagementConfigUtils;
import org.springframework.transaction.event.TransactionalEventListenerFactory;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.testfixture.CallCountingTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
@@ -99,6 +103,9 @@ public class EnableTransactionManagementTests {
assertThat(txManager.begun).isEqualTo(1);
assertThat(txManager.commits).isEqualTo(1);
assertThat(txManager.rollbacks).isEqualTo(0);
assertThat(txManager.lastDefinition.isReadOnly()).isTrue();
assertThat(txManager.lastDefinition.getTimeout()).isEqualTo(5);
assertThat(((TransactionAttribute) txManager.lastDefinition).getLabels()).contains("LABEL");
ctx.close();
}
@@ -266,7 +273,7 @@ public class EnableTransactionManagementTests {
@Service
public static class TransactionalTestBean {
@Transactional(readOnly = true)
@Transactional(label = "${myLabel}", timeoutString = "${myTimeout}", readOnly = true)
public Collection<?> findAllFoos() {
return null;
}
@@ -275,14 +282,31 @@ public class EnableTransactionManagementTests {
public void saveQualifiedFoo() {
}
@Transactional(transactionManager = "qualifiedTransactionManager")
@Transactional(transactionManager = "${myTransactionManager}")
public void saveQualifiedFooWithAttributeAlias() {
}
}
@Configuration
static class PlaceholderConfig {
@Bean
public PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
Properties props = new Properties();
props.setProperty("myLabel", "LABEL");
props.setProperty("myTimeout", "5");
props.setProperty("myTransactionManager", "qualifiedTransactionManager");
pspc.setProperties(props);
return pspc;
}
}
@Configuration
@EnableTransactionManagement
@Import(PlaceholderConfig.class)
static class EnableTxConfig {
}
@@ -294,6 +318,7 @@ public class EnableTransactionManagementTests {
@Configuration
@EnableTransactionManagement
@Import(PlaceholderConfig.class)
@Conditional(NeverCondition.class)
static class ParentEnableTxConfig {
@@ -433,6 +458,7 @@ public class EnableTransactionManagementTests {
@Configuration
@EnableTransactionManagement
@Import(PlaceholderConfig.class)
static class Spr11915Config {
@Autowired