diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java
index a85063a6d4..b9ed551b74 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java
@@ -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.
@@ -30,6 +30,8 @@ import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
* Strategy implementation for parsing Spring's {@link Transactional} annotation.
@@ -70,7 +72,13 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
+
rbta.setTimeout(attributes.getNumber("timeout").intValue());
+ String timeoutString = attributes.getString("timeoutString");
+ Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
+ "Specify 'timeout' or 'timeoutString', not both");
+ rbta.setTimeoutString(timeoutString);
+
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
index e4eda9d117..4af6be7675 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
@@ -84,6 +84,18 @@ public @interface Transactional {
@AliasFor("value")
String transactionManager() default "";
+ /**
+ * Defines zero (0) or more transaction labels. Labels may be used to
+ * describe a transaction and they can be evaluated by individual transaction
+ * manager. Labels may serve a solely descriptive purpose or map to
+ * pre-defined transaction manager-specific options.
+ *
See the description of the actual transaction manager implementation
+ * how it evaluates transaction labels.
+ * @since 5.3
+ * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#getLabels()
+ */
+ String[] label() default {};
+
/**
* The transaction propagation type.
*
Defaults to {@link Propagation#REQUIRED}.
@@ -111,10 +123,23 @@ public @interface Transactional {
*
Exclusively designed for use with {@link Propagation#REQUIRED} or
* {@link Propagation#REQUIRES_NEW} since it only applies to newly started
* transactions.
+ * @return the timeout in seconds
* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
+ /**
+ * The timeout for this transaction (in seconds).
+ *
Defaults to the default timeout of the underlying transaction system.
+ *
Exclusively designed for use with {@link Propagation#REQUIRED} or
+ * {@link Propagation#REQUIRES_NEW} since it only applies to newly started
+ * transactions.
+ * @return the timeout in seconds as a String value, e.g. a placeholder
+ * @since 5.3
+ * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
+ */
+ String timeoutString() default "";
+
/**
* A boolean flag that can be set to {@code true} if the transaction is
* effectively read-only, allowing for corresponding optimizations at runtime.
@@ -190,17 +215,4 @@ public @interface Transactional {
*/
String[] noRollbackForClassName() default {};
- /**
- * Defines zero (0) or more transaction labels. Labels may be used to
- * describe a transaction and they can be evaluated by individual transaction
- * manager. Labels may serve a solely descriptive purpose or map to
- * pre-defined transaction manager-specific options.
- *
See the description of the actual transaction manager implementation
- * how it evaluates transaction labels.
- *
- * @since 5.3
- * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#getLabels()
- */
- String[] label() default {};
-
}
diff --git a/spring-tx/src/main/java/org/springframework/transaction/config/TxAdviceBeanDefinitionParser.java b/spring-tx/src/main/java/org/springframework/transaction/config/TxAdviceBeanDefinitionParser.java
index 41a1e61370..171b626109 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/config/TxAdviceBeanDefinitionParser.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/config/TxAdviceBeanDefinitionParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 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.
@@ -116,12 +116,7 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
}
if (StringUtils.hasText(timeout)) {
- try {
- attribute.setTimeout(Integer.parseInt(timeout));
- }
- catch (NumberFormatException ex) {
- parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
- }
+ attribute.setTimeoutString(timeout);
}
if (StringUtils.hasText(readOnly)) {
attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java
index 9ea4456bac..80983806ed 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 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.
@@ -25,9 +25,11 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
+import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
+import org.springframework.util.StringValueResolver;
/**
* Abstract implementation of {@link TransactionAttributeSource} that caches
@@ -49,7 +51,8 @@ import org.springframework.util.ClassUtils;
* @author Juergen Hoeller
* @since 1.1
*/
-public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
+public abstract class AbstractFallbackTransactionAttributeSource
+ implements TransactionAttributeSource, EmbeddedValueResolverAware {
/**
* Canonical value held in cache to indicate no transaction attribute was
@@ -71,6 +74,9 @@ public abstract class AbstractFallbackTransactionAttributeSource implements Tran
*/
protected final Log logger = LogFactory.getLog(getClass());
+ @Nullable
+ private transient StringValueResolver embeddedValueResolver;
+
/**
* Cache of TransactionAttributes, keyed by method on a specific target class.
*
As this base class is not marked Serializable, the cache will be recreated
@@ -79,6 +85,12 @@ public abstract class AbstractFallbackTransactionAttributeSource implements Tran
private final Map