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:
parent
273d952ddf
commit
dd0d0d51f6
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.RollbackRuleAttribute;
|
||||||
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
|
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
|
||||||
import org.springframework.transaction.interceptor.TransactionAttribute;
|
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.
|
* Strategy implementation for parsing Spring's {@link Transactional} annotation.
|
||||||
|
@ -70,7 +72,13 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
|
||||||
rbta.setPropagationBehavior(propagation.value());
|
rbta.setPropagationBehavior(propagation.value());
|
||||||
Isolation isolation = attributes.getEnum("isolation");
|
Isolation isolation = attributes.getEnum("isolation");
|
||||||
rbta.setIsolationLevel(isolation.value());
|
rbta.setIsolationLevel(isolation.value());
|
||||||
|
|
||||||
rbta.setTimeout(attributes.getNumber("timeout").intValue());
|
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.setReadOnly(attributes.getBoolean("readOnly"));
|
||||||
rbta.setQualifier(attributes.getString("value"));
|
rbta.setQualifier(attributes.getString("value"));
|
||||||
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
|
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
|
||||||
|
|
|
@ -84,6 +84,18 @@ public @interface Transactional {
|
||||||
@AliasFor("value")
|
@AliasFor("value")
|
||||||
String transactionManager() default "";
|
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.
|
||||||
|
* <p>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.
|
* The transaction propagation type.
|
||||||
* <p>Defaults to {@link Propagation#REQUIRED}.
|
* <p>Defaults to {@link Propagation#REQUIRED}.
|
||||||
|
@ -111,10 +123,23 @@ public @interface Transactional {
|
||||||
* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or
|
* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or
|
||||||
* {@link Propagation#REQUIRES_NEW} since it only applies to newly started
|
* {@link Propagation#REQUIRES_NEW} since it only applies to newly started
|
||||||
* transactions.
|
* transactions.
|
||||||
|
* @return the timeout in seconds
|
||||||
* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
|
* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
|
||||||
*/
|
*/
|
||||||
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
|
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timeout for this transaction (in seconds).
|
||||||
|
* <p>Defaults to the default timeout of the underlying transaction system.
|
||||||
|
* <p>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
|
* A boolean flag that can be set to {@code true} if the transaction is
|
||||||
* effectively read-only, allowing for corresponding optimizations at runtime.
|
* effectively read-only, allowing for corresponding optimizations at runtime.
|
||||||
|
@ -190,17 +215,4 @@ public @interface Transactional {
|
||||||
*/
|
*/
|
||||||
String[] noRollbackForClassName() default {};
|
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.
|
|
||||||
* <p>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 {};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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);
|
attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
|
||||||
}
|
}
|
||||||
if (StringUtils.hasText(timeout)) {
|
if (StringUtils.hasText(timeout)) {
|
||||||
try {
|
attribute.setTimeoutString(timeout);
|
||||||
attribute.setTimeout(Integer.parseInt(timeout));
|
|
||||||
}
|
|
||||||
catch (NumberFormatException ex) {
|
|
||||||
parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (StringUtils.hasText(readOnly)) {
|
if (StringUtils.hasText(readOnly)) {
|
||||||
attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
|
attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
import org.springframework.core.MethodClassKey;
|
import org.springframework.core.MethodClassKey;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract implementation of {@link TransactionAttributeSource} that caches
|
* Abstract implementation of {@link TransactionAttributeSource} that caches
|
||||||
|
@ -49,7 +51,8 @@ import org.springframework.util.ClassUtils;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 1.1
|
* @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
|
* 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());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private transient StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of TransactionAttributes, keyed by method on a specific target class.
|
* Cache of TransactionAttributes, keyed by method on a specific target class.
|
||||||
* <p>As this base class is not marked Serializable, the cache will be recreated
|
* <p>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<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);
|
private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||||
|
this.embeddedValueResolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the transaction attribute for this method invocation.
|
* Determine the transaction attribute for this method invocation.
|
||||||
* <p>Defaults to the class's transaction attribute if no method attribute is found.
|
* <p>Defaults to the class's transaction attribute if no method attribute is found.
|
||||||
|
@ -117,7 +129,9 @@ public abstract class AbstractFallbackTransactionAttributeSource implements Tran
|
||||||
else {
|
else {
|
||||||
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
|
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
|
||||||
if (txAttr instanceof DefaultTransactionAttribute) {
|
if (txAttr instanceof DefaultTransactionAttribute) {
|
||||||
((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
|
DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr;
|
||||||
|
dta.setDescriptor(methodIdentification);
|
||||||
|
dta.resolveAttributeStrings(this.embeddedValueResolver);
|
||||||
}
|
}
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
|
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,10 +18,13 @@ package org.springframework.transaction.interceptor;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring's common transaction attribute implementation.
|
* Spring's common transaction attribute implementation.
|
||||||
|
@ -36,10 +39,13 @@ import org.springframework.util.StringUtils;
|
||||||
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
|
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String qualifier;
|
private String descriptor;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String descriptor;
|
private String timeoutString;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String qualifier;
|
||||||
|
|
||||||
private Collection<String> labels = Collections.emptyList();
|
private Collection<String> labels = Collections.emptyList();
|
||||||
|
|
||||||
|
@ -83,11 +89,54 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a descriptor for this transaction attribute,
|
||||||
|
* e.g. indicating where the attribute is applying.
|
||||||
|
* @since 4.3.4
|
||||||
|
*/
|
||||||
|
public void setDescriptor(@Nullable String descriptor) {
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a descriptor for this transaction attribute,
|
||||||
|
* or {@code null} if none.
|
||||||
|
* @since 4.3.4
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getDescriptor() {
|
||||||
|
return this.descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the timeout to apply, if any,
|
||||||
|
* as a String value that resolves to a number of seconds.
|
||||||
|
* @since 5.3
|
||||||
|
* @see #setTimeout
|
||||||
|
* @see #resolveAttributeStrings
|
||||||
|
*/
|
||||||
|
public void setTimeoutString(@Nullable String timeoutString) {
|
||||||
|
this.timeoutString = timeoutString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the timeout to apply, if any,
|
||||||
|
* as a String value that resolves to a number of seconds.
|
||||||
|
* @since 5.3
|
||||||
|
* @see #getTimeout
|
||||||
|
* @see #resolveAttributeStrings
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getTimeoutString() {
|
||||||
|
return this.timeoutString;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a qualifier value with this transaction attribute.
|
* Associate a qualifier value with this transaction attribute.
|
||||||
* <p>This may be used for choosing a corresponding transaction manager
|
* <p>This may be used for choosing a corresponding transaction manager
|
||||||
* to process this specific transaction.
|
* to process this specific transaction.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see #resolveAttributeStrings
|
||||||
*/
|
*/
|
||||||
public void setQualifier(@Nullable String qualifier) {
|
public void setQualifier(@Nullable String qualifier) {
|
||||||
this.qualifier = qualifier;
|
this.qualifier = qualifier;
|
||||||
|
@ -108,6 +157,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||||
* <p>This may be used for applying specific transactional behavior
|
* <p>This may be used for applying specific transactional behavior
|
||||||
* or follow a purely descriptive nature.
|
* or follow a purely descriptive nature.
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
|
* @see #resolveAttributeStrings
|
||||||
*/
|
*/
|
||||||
public void setLabels(Collection<String> labels) {
|
public void setLabels(Collection<String> labels) {
|
||||||
this.labels = labels;
|
this.labels = labels;
|
||||||
|
@ -118,25 +168,6 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||||
return this.labels;
|
return this.labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a descriptor for this transaction attribute,
|
|
||||||
* e.g. indicating where the attribute is applying.
|
|
||||||
* @since 4.3.4
|
|
||||||
*/
|
|
||||||
public void setDescriptor(@Nullable String descriptor) {
|
|
||||||
this.descriptor = descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a descriptor for this transaction attribute,
|
|
||||||
* or {@code null} if none.
|
|
||||||
* @since 4.3.4
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public String getDescriptor() {
|
|
||||||
return this.descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default behavior is as with EJB: rollback on unchecked exception
|
* The default behavior is as with EJB: rollback on unchecked exception
|
||||||
* ({@link RuntimeException}), assuming an unexpected outcome outside of any
|
* ({@link RuntimeException}), assuming an unexpected outcome outside of any
|
||||||
|
@ -157,6 +188,42 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve attribute values that are defined as resolvable Strings:
|
||||||
|
* {@link #setTimeoutString}, {@link #setQualifier}, {@link #setLabels}.
|
||||||
|
* This is typically used for resolving "${...}" placeholders.
|
||||||
|
* @param resolver the embedded value resolver to apply, if any
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
public void resolveAttributeStrings(@Nullable StringValueResolver resolver) {
|
||||||
|
String timeoutString = this.timeoutString;
|
||||||
|
if (StringUtils.hasText(timeoutString)) {
|
||||||
|
if (resolver != null) {
|
||||||
|
timeoutString = resolver.resolveStringValue(timeoutString);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasLength(timeoutString)) {
|
||||||
|
try {
|
||||||
|
setTimeout(Integer.parseInt(timeoutString));
|
||||||
|
}
|
||||||
|
catch (RuntimeException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid timeoutString value \"" + timeoutString + "\" - cannot parse into int");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolver != null) {
|
||||||
|
if (this.qualifier != null) {
|
||||||
|
this.qualifier = resolver.resolveStringValue(this.qualifier);
|
||||||
|
}
|
||||||
|
Set<String> resolvedLabels = new LinkedHashSet<>(this.labels.size());
|
||||||
|
for (String label : this.labels) {
|
||||||
|
resolvedLabels.add(resolver.resolveStringValue(label));
|
||||||
|
}
|
||||||
|
this.labels = resolvedLabels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an identifying description for this transaction attribute.
|
* Return an identifying description for this transaction attribute.
|
||||||
* <p>Available to subclasses, for inclusion in their {@code toString()} result.
|
* <p>Available to subclasses, for inclusion in their {@code toString()} result.
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -31,6 +31,7 @@ import org.springframework.util.ObjectUtils;
|
||||||
* methods being handled by a transaction interceptor.
|
* methods being handled by a transaction interceptor.
|
||||||
*
|
*
|
||||||
* @author Colin Sampaleanu
|
* @author Colin Sampaleanu
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 15.10.2003
|
* @since 15.10.2003
|
||||||
* @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean
|
* @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean
|
||||||
* @see org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
|
* @see org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
|
||||||
|
@ -48,6 +49,9 @@ public class MatchAlwaysTransactionAttributeSource implements TransactionAttribu
|
||||||
* @see org.springframework.transaction.interceptor.TransactionAttributeEditor
|
* @see org.springframework.transaction.interceptor.TransactionAttributeEditor
|
||||||
*/
|
*/
|
||||||
public void setTransactionAttribute(TransactionAttribute transactionAttribute) {
|
public void setTransactionAttribute(TransactionAttribute transactionAttribute) {
|
||||||
|
if (transactionAttribute instanceof DefaultTransactionAttribute) {
|
||||||
|
((DefaultTransactionAttribute) transactionAttribute).resolveAttributeStrings(null);
|
||||||
|
}
|
||||||
this.transactionAttribute = transactionAttribute;
|
this.transactionAttribute = transactionAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -27,11 +27,13 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.PatternMatchUtils;
|
import org.springframework.util.PatternMatchUtils;
|
||||||
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple {@link TransactionAttributeSource} implementation that
|
* Simple {@link TransactionAttributeSource} implementation that
|
||||||
|
@ -44,7 +46,7 @@ import org.springframework.util.PatternMatchUtils;
|
||||||
* @see NameMatchTransactionAttributeSource
|
* @see NameMatchTransactionAttributeSource
|
||||||
*/
|
*/
|
||||||
public class MethodMapTransactionAttributeSource
|
public class MethodMapTransactionAttributeSource
|
||||||
implements TransactionAttributeSource, BeanClassLoaderAware, InitializingBean {
|
implements TransactionAttributeSource, EmbeddedValueResolverAware, BeanClassLoaderAware, InitializingBean {
|
||||||
|
|
||||||
/** Logger available to subclasses. */
|
/** Logger available to subclasses. */
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
@ -53,6 +55,9 @@ public class MethodMapTransactionAttributeSource
|
||||||
@Nullable
|
@Nullable
|
||||||
private Map<String, TransactionAttribute> methodMap;
|
private Map<String, TransactionAttribute> methodMap;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||||
|
|
||||||
|
@ -83,6 +88,11 @@ public class MethodMapTransactionAttributeSource
|
||||||
this.methodMap = methodMap;
|
this.methodMap = methodMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||||
|
this.embeddedValueResolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||||
this.beanClassLoader = beanClassLoader;
|
this.beanClassLoader = beanClassLoader;
|
||||||
|
@ -189,6 +199,9 @@ public class MethodMapTransactionAttributeSource
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Adding transactional method [" + method + "] with attribute [" + attr + "]");
|
logger.debug("Adding transactional method [" + method + "] with attribute [" + attr + "]");
|
||||||
}
|
}
|
||||||
|
if (this.embeddedValueResolver != null && attr instanceof DefaultTransactionAttribute) {
|
||||||
|
((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver);
|
||||||
|
}
|
||||||
this.transactionAttributeMap.put(method, attr);
|
this.transactionAttributeMap.put(method, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -26,10 +26,13 @@ import java.util.Properties;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.PatternMatchUtils;
|
import org.springframework.util.PatternMatchUtils;
|
||||||
|
import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple {@link TransactionAttributeSource} implementation that
|
* Simple {@link TransactionAttributeSource} implementation that
|
||||||
|
@ -41,7 +44,8 @@ import org.springframework.util.PatternMatchUtils;
|
||||||
* @see MethodMapTransactionAttributeSource
|
* @see MethodMapTransactionAttributeSource
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class NameMatchTransactionAttributeSource implements TransactionAttributeSource, Serializable {
|
public class NameMatchTransactionAttributeSource
|
||||||
|
implements TransactionAttributeSource, EmbeddedValueResolverAware, InitializingBean, Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger available to subclasses.
|
* Logger available to subclasses.
|
||||||
|
@ -50,7 +54,10 @@ public class NameMatchTransactionAttributeSource implements TransactionAttribute
|
||||||
protected static final Log logger = LogFactory.getLog(NameMatchTransactionAttributeSource.class);
|
protected static final Log logger = LogFactory.getLog(NameMatchTransactionAttributeSource.class);
|
||||||
|
|
||||||
/** Keys are method names; values are TransactionAttributes. */
|
/** Keys are method names; values are TransactionAttributes. */
|
||||||
private Map<String, TransactionAttribute> nameMap = new HashMap<>();
|
private final Map<String, TransactionAttribute> nameMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,9 +101,26 @@ public class NameMatchTransactionAttributeSource implements TransactionAttribute
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
|
logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
|
||||||
}
|
}
|
||||||
|
if (this.embeddedValueResolver != null && attr instanceof DefaultTransactionAttribute) {
|
||||||
|
((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver);
|
||||||
|
}
|
||||||
this.nameMap.put(methodName, attr);
|
this.nameMap.put(methodName, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||||
|
this.embeddedValueResolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
for (TransactionAttribute attr : this.nameMap.values()) {
|
||||||
|
if (attr instanceof DefaultTransactionAttribute) {
|
||||||
|
((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -69,7 +69,7 @@ public class TransactionAttributeEditor extends PropertyEditorSupport {
|
||||||
}
|
}
|
||||||
else if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_TIMEOUT)) {
|
else if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_TIMEOUT)) {
|
||||||
String value = trimmedToken.substring(DefaultTransactionAttribute.PREFIX_TIMEOUT.length());
|
String value = trimmedToken.substring(DefaultTransactionAttribute.PREFIX_TIMEOUT.length());
|
||||||
attr.setTimeout(Integer.parseInt(value));
|
attr.setTimeoutString(value);
|
||||||
}
|
}
|
||||||
else if (trimmedToken.equals(RuleBasedTransactionAttribute.READ_ONLY_MARKER)) {
|
else if (trimmedToken.equals(RuleBasedTransactionAttribute.READ_ONLY_MARKER)) {
|
||||||
attr.setReadOnly(true);
|
attr.setReadOnly(true);
|
||||||
|
@ -84,6 +84,7 @@ public class TransactionAttributeEditor extends PropertyEditorSupport {
|
||||||
throw new IllegalArgumentException("Invalid transaction attribute token: [" + trimmedToken + "]");
|
throw new IllegalArgumentException("Invalid transaction attribute token: [" + trimmedToken + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
attr.resolveAttributeStrings(null); // placeholders expected to be pre-resolved
|
||||||
setValue(attr);
|
setValue(attr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -218,7 +218,7 @@
|
||||||
</xsd:restriction>
|
</xsd:restriction>
|
||||||
</xsd:simpleType>
|
</xsd:simpleType>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
<xsd:attribute name="timeout" type="xsd:int" default="-1">
|
<xsd:attribute name="timeout" type="xsd:string" default="">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation><![CDATA[
|
<xsd:documentation><![CDATA[
|
||||||
The transaction timeout value (in seconds).
|
The transaction timeout value (in seconds).
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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);
|
assertThat(ptm.begun).as("Should not have any started transactions").isEqualTo(0);
|
||||||
testBean.getName();
|
testBean.getName();
|
||||||
assertThat(ptm.lastDefinition.isReadOnly()).isTrue();
|
assertThat(ptm.lastDefinition.isReadOnly()).isTrue();
|
||||||
|
assertThat(ptm.lastDefinition.getTimeout()).isEqualTo(5);
|
||||||
assertThat(ptm.begun).as("Should have 1 started transaction").isEqualTo(1);
|
assertThat(ptm.begun).as("Should have 1 started transaction").isEqualTo(1);
|
||||||
assertThat(ptm.commits).as("Should have 1 committed transaction").isEqualTo(1);
|
assertThat(ptm.commits).as("Should have 1 committed transaction").isEqualTo(1);
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -140,22 +140,31 @@ public class AnnotationTransactionAttributeSourceTests {
|
||||||
@Test
|
@Test
|
||||||
public void transactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception {
|
public void transactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception {
|
||||||
Method interfaceMethod = ITestBean3.class.getMethod("getAge");
|
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();
|
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
|
||||||
|
atas.setEmbeddedValueResolver(strVal -> ("${myTimeout}".equals(strVal) ? "5" : strVal));
|
||||||
|
|
||||||
TransactionAttribute actual = atas.getTransactionAttribute(interfaceMethod, TestBean3.class);
|
TransactionAttribute actual = atas.getTransactionAttribute(interfaceMethod, TestBean3.class);
|
||||||
assertThat(actual.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRES_NEW);
|
assertThat(actual.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRES_NEW);
|
||||||
assertThat(actual.getIsolationLevel()).isEqualTo(TransactionAttribute.ISOLATION_REPEATABLE_READ);
|
assertThat(actual.getIsolationLevel()).isEqualTo(TransactionAttribute.ISOLATION_REPEATABLE_READ);
|
||||||
assertThat(actual.getTimeout()).isEqualTo(5);
|
assertThat(actual.getTimeout()).isEqualTo(5);
|
||||||
assertThat(actual.isReadOnly()).isTrue();
|
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();
|
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
|
||||||
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
|
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
|
||||||
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
|
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
|
||||||
assertThat(((RuleBasedTransactionAttribute) actual).getRollbackRules()).isEqualTo(rbta.getRollbackRules());
|
assertThat(((RuleBasedTransactionAttribute) actual).getRollbackRules()).isEqualTo(rbta.getRollbackRules());
|
||||||
|
|
||||||
TransactionAttribute actual2 = atas.getTransactionAttribute(interfaceMethod2, TestBean3.class);
|
TransactionAttribute actual3 = atas.getTransactionAttribute(interfaceMethod3, TestBean3.class);
|
||||||
assertThat(actual2.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRED);
|
assertThat(actual3.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -585,6 +594,9 @@ public class AnnotationTransactionAttributeSourceTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
public void setAge(int age) {
|
||||||
this.age = age;
|
this.age = age;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.transaction.annotation;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
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.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ConfigurationCondition;
|
import org.springframework.context.annotation.ConfigurationCondition;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.TransactionManager;
|
import org.springframework.transaction.TransactionManager;
|
||||||
import org.springframework.transaction.config.TransactionManagementConfigUtils;
|
import org.springframework.transaction.config.TransactionManagementConfigUtils;
|
||||||
import org.springframework.transaction.event.TransactionalEventListenerFactory;
|
import org.springframework.transaction.event.TransactionalEventListenerFactory;
|
||||||
|
import org.springframework.transaction.interceptor.TransactionAttribute;
|
||||||
import org.springframework.transaction.testfixture.CallCountingTransactionManager;
|
import org.springframework.transaction.testfixture.CallCountingTransactionManager;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -99,6 +103,9 @@ public class EnableTransactionManagementTests {
|
||||||
assertThat(txManager.begun).isEqualTo(1);
|
assertThat(txManager.begun).isEqualTo(1);
|
||||||
assertThat(txManager.commits).isEqualTo(1);
|
assertThat(txManager.commits).isEqualTo(1);
|
||||||
assertThat(txManager.rollbacks).isEqualTo(0);
|
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();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
@ -266,7 +273,7 @@ public class EnableTransactionManagementTests {
|
||||||
@Service
|
@Service
|
||||||
public static class TransactionalTestBean {
|
public static class TransactionalTestBean {
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(label = "${myLabel}", timeoutString = "${myTimeout}", readOnly = true)
|
||||||
public Collection<?> findAllFoos() {
|
public Collection<?> findAllFoos() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -275,14 +282,31 @@ public class EnableTransactionManagementTests {
|
||||||
public void saveQualifiedFoo() {
|
public void saveQualifiedFoo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(transactionManager = "qualifiedTransactionManager")
|
@Transactional(transactionManager = "${myTransactionManager}")
|
||||||
public void saveQualifiedFooWithAttributeAlias() {
|
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
|
@Configuration
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
|
@Import(PlaceholderConfig.class)
|
||||||
static class EnableTxConfig {
|
static class EnableTxConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,6 +318,7 @@ public class EnableTransactionManagementTests {
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
|
@Import(PlaceholderConfig.class)
|
||||||
@Conditional(NeverCondition.class)
|
@Conditional(NeverCondition.class)
|
||||||
static class ParentEnableTxConfig {
|
static class ParentEnableTxConfig {
|
||||||
|
|
||||||
|
@ -433,6 +458,7 @@ public class EnableTransactionManagementTests {
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
|
@Import(PlaceholderConfig.class)
|
||||||
static class Spr11915Config {
|
static class Spr11915Config {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -7,13 +7,17 @@
|
||||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.5.xsd
|
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.5.xsd
|
||||||
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
|
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
|
||||||
|
|
||||||
|
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
|
||||||
|
<property name="properties" value="myTimeout=5"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<aop:config>
|
<aop:config>
|
||||||
<aop:advisor pointcut="execution(* *..ITestBean.*(..))" advice-ref="txAdvice"/>
|
<aop:advisor pointcut="execution(* *..ITestBean.*(..))" advice-ref="txAdvice"/>
|
||||||
</aop:config>
|
</aop:config>
|
||||||
|
|
||||||
<tx:advice id="txAdvice">
|
<tx:advice id="txAdvice">
|
||||||
<tx:attributes>
|
<tx:attributes>
|
||||||
<tx:method name="get*" read-only="true"/>
|
<tx:method name="get*" timeout="5" read-only="true"/>
|
||||||
<tx:method name="set*"/>
|
<tx:method name="set*"/>
|
||||||
<tx:method name="exceptional"/>
|
<tx:method name="exceptional"/>
|
||||||
</tx:attributes>
|
</tx:attributes>
|
||||||
|
|
Loading…
Reference in New Issue