Support @Rollback on classes & deprecate @TxConfig
Since Spring Framework 2.5, @Rollback has been supported on test methods, with class-level rollback settings configured via @TransactionConfiguration; however, allowing @Rollback to be declared on test classes with method-level declarations overriding class-level declarations would prove more intuitive than having to declare both @TransactionConfiguration and @Rollback. Furthermore, the transactionManager flag in @TransactionConfiguration was made superfluous many years ago with the introduction of support for a qualifier in @Transactional. This commit enables @Rollback to be declared at the class level for default rollback semantics within test class hierarchies and deprecates @TransactionConfiguration in favor of @Rollback and @Transactional qualifiers. Issue: SPR-13276, SPR-13277
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -24,25 +24,36 @@ import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
|
||||
/**
|
||||
* Test annotation used to indicate whether or not the transaction for the
|
||||
* annotated test method should be <em>rolled back</em> after the test method
|
||||
* has completed. If {@code true}, the transaction will be rolled back;
|
||||
* otherwise, the transaction will be committed.
|
||||
* Test annotation used to indicate whether a <em>test-managed transaction</em>
|
||||
* should be <em>rolled back</em> after the test method has completed.
|
||||
*
|
||||
* <p>Consult the class-level Javadoc for
|
||||
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
|
||||
* for an explanation of <em>test-managed transactions</em>.
|
||||
*
|
||||
* <p>When declared as a class-level annotation, {@code @Rollback} defines
|
||||
* the default rollback semantics for all test methods within the test class
|
||||
* hierarchy. When declared as a method-level annotation, {@code @Rollback}
|
||||
* defines rollback semantics for the specific test method, potentially
|
||||
* overriding class-level default rollback semantics.
|
||||
*
|
||||
* <p>As of Spring Framework 4.0, this annotation may be used as a
|
||||
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ METHOD, ANNOTATION_TYPE })
|
||||
@Target({ TYPE, METHOD, ANNOTATION_TYPE })
|
||||
public @interface Rollback {
|
||||
|
||||
/**
|
||||
* Whether or not the transaction for the annotated method should be rolled
|
||||
* back after the method has completed.
|
||||
* Whether the <em>test-managed transaction</em> should be rolled back
|
||||
* after the test method has completed.
|
||||
* <p>If {@code true}, the transaction will be rolled back; otherwise,
|
||||
* the transaction will be committed.
|
||||
*/
|
||||
boolean value() default true;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -36,8 +36,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for working with transactions and data access related beans
|
||||
* within the <em>Spring TestContext Framework</em>. Mainly for internal use
|
||||
* within the framework.
|
||||
* within the <em>Spring TestContext Framework</em>.
|
||||
* <p>Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
@@ -146,6 +146,8 @@ public abstract class TestContextTransactionUtils {
|
||||
* @return the transaction manager to use, or {@code null} if not found
|
||||
* @throws BeansException if an error occurs while retrieving an explicitly
|
||||
* named transaction manager
|
||||
* @throws IllegalStateException if more than one TransactionManagementConfigurer
|
||||
* exists in the ApplicationContext
|
||||
*/
|
||||
public static PlatformTransactionManager retrieveTransactionManager(TestContext testContext, String name) {
|
||||
Assert.notNull(testContext, "TestContext must not be null");
|
||||
|
||||
@@ -39,7 +39,10 @@ import java.lang.annotation.Target;
|
||||
* @see org.springframework.test.context.jdbc.SqlConfig
|
||||
* @see org.springframework.test.context.jdbc.SqlConfig#transactionManager
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @deprecated As of Spring Framework 4.2, use {@code @Rollback} at the class
|
||||
* level and the {@code transactionManager} qualifier in {@code @Transactional}.
|
||||
*/
|
||||
@Deprecated
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -48,7 +51,7 @@ public @interface TransactionConfiguration {
|
||||
|
||||
/**
|
||||
* The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
|
||||
* PlatformTransactionManager} that should be used to drive transactions.
|
||||
* PlatformTransactionManager} that should be used to drive <em>test-managed transactions</em>.
|
||||
*
|
||||
* <p>The name is only used if there is more than one bean of type
|
||||
* {@code PlatformTransactionManager} in the test's {@code ApplicationContext}.
|
||||
@@ -76,7 +79,7 @@ public @interface TransactionConfiguration {
|
||||
String transactionManager() default "";
|
||||
|
||||
/**
|
||||
* Should transactions be rolled back by default?
|
||||
* Whether <em>test-managed transactions</em> should be rolled back by default.
|
||||
*/
|
||||
boolean defaultRollback() default true;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -36,14 +36,27 @@ public class TransactionConfigurationAttributes {
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new TransactionConfigurationAttributes instance from the
|
||||
* supplied arguments.
|
||||
* Construct a new {@code TransactionConfigurationAttributes} instance
|
||||
* using an empty string for the bean name of the
|
||||
* {@link PlatformTransactionManager} and {@code true} for the default
|
||||
* rollback flag.
|
||||
* @see #TransactionConfigurationAttributes(String, boolean)
|
||||
*/
|
||||
public TransactionConfigurationAttributes() {
|
||||
this("", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code TransactionConfigurationAttributes} instance
|
||||
* from the supplied arguments.
|
||||
* @param transactionManagerName the bean name of the
|
||||
* {@link PlatformTransactionManager} that is to be used to drive transactions
|
||||
* @param defaultRollback whether or not transactions should be rolled back by default
|
||||
* {@link PlatformTransactionManager} that is to be used to drive
|
||||
* <em>test-managed transactions</em>
|
||||
* @param defaultRollback whether or not <em>test-managed transactions</em>
|
||||
* should be rolled back by default
|
||||
*/
|
||||
public TransactionConfigurationAttributes(String transactionManagerName, boolean defaultRollback) {
|
||||
Assert.notNull(transactionManagerName, "transactionManagerName can not be null");
|
||||
Assert.notNull(transactionManagerName, "transactionManagerName must not be null");
|
||||
this.transactionManagerName = transactionManagerName;
|
||||
this.defaultRollback = defaultRollback;
|
||||
}
|
||||
@@ -51,14 +64,14 @@ public class TransactionConfigurationAttributes {
|
||||
|
||||
/**
|
||||
* Get the bean name of the {@link PlatformTransactionManager} that is to
|
||||
* be used to drive transactions.
|
||||
* be used to drive <em>test-managed transactions</em>.
|
||||
*/
|
||||
public final String getTransactionManagerName() {
|
||||
return this.transactionManagerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not transactions should be rolled back by default.
|
||||
* Whether <em>test-managed transactions</em> should be rolled back by default.
|
||||
* @return the <em>default rollback</em> flag
|
||||
*/
|
||||
public final boolean isDefaultRollback() {
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.annotation.Rollback;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
@@ -84,9 +83,8 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
* <h3>Declarative Rollback and Commit Behavior</h3>
|
||||
* <p>By default, test transactions will be automatically <em>rolled back</em>
|
||||
* after completion of the test; however, transactional commit and rollback
|
||||
* behavior can be configured declaratively via the class-level
|
||||
* {@link TransactionConfiguration @TransactionConfiguration} and method-level
|
||||
* {@link Rollback @Rollback} annotations.
|
||||
* behavior can be configured declaratively via the {@link Rollback @Rollback}
|
||||
* annotation at the class level and at the method level.
|
||||
*
|
||||
* <h3>Programmatic Transaction Management</h3>
|
||||
* <p>As of Spring Framework 4.1, it is possible to interact with test-managed
|
||||
@@ -107,13 +105,11 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
* {@link PlatformTransactionManager} bean to be defined in the Spring
|
||||
* {@code ApplicationContext} for the test. In case there are multiple
|
||||
* instances of {@code PlatformTransactionManager} within the test's
|
||||
* {@code ApplicationContext}, {@code @TransactionConfiguration} supports
|
||||
* configuring the bean name of the {@code PlatformTransactionManager} that
|
||||
* should be used to drive transactions. Alternatively, a <em>qualifier</em>
|
||||
* may be declared via
|
||||
* {@link org.springframework.transaction.annotation.Transactional#value @Transactional("myQualifier")}, or
|
||||
* {@link org.springframework.transaction.annotation.TransactionManagementConfigurer TransactionManagementConfigurer}
|
||||
* can be implemented by an
|
||||
* {@code ApplicationContext}, a <em>qualifier</em> may be declared via
|
||||
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
|
||||
* (e.g., {@code @Transactional("myTxMgr")} or {@code @Transactional(transactionManger = "myTxMgr")},
|
||||
* or {@link org.springframework.transaction.annotation.TransactionManagementConfigurer
|
||||
* TransactionManagementConfigurer} can be implemented by an
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* class. See {@link TestContextTransactionUtils#retrieveTransactionManager}
|
||||
* for details on the algorithm used to look up a transaction manager in
|
||||
@@ -122,7 +118,6 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TransactionConfiguration
|
||||
* @see org.springframework.transaction.annotation.TransactionManagementConfigurer
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
* @see org.springframework.test.annotation.Rollback
|
||||
@@ -134,8 +129,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TransactionalTestExecutionListener.class);
|
||||
|
||||
private static final TransactionConfiguration defaultTransactionConfiguration =
|
||||
AnnotationUtils.synthesizeAnnotation(TransactionConfiguration.class);
|
||||
private static final TransactionConfigurationAttributes defaultTxConfigAttributes = new TransactionConfigurationAttributes();
|
||||
|
||||
protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
|
||||
|
||||
@@ -229,8 +223,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
|
||||
/**
|
||||
* Run all {@link BeforeTransaction @BeforeTransaction} methods for the
|
||||
* specified {@link TestContext test context}. If one of the methods fails,
|
||||
* however, the caught exception will be rethrown in a wrapped
|
||||
* specified {@linkplain TestContext test context}. If one of the methods
|
||||
* fails, however, the caught exception will be rethrown in a wrapped
|
||||
* {@link RuntimeException}, and the remaining methods will <strong>not</strong>
|
||||
* be given a chance to execute.
|
||||
* @param testContext the current test context
|
||||
@@ -255,8 +249,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
|
||||
/**
|
||||
* Run all {@link AfterTransaction @AfterTransaction} methods for the
|
||||
* specified {@link TestContext test context}. If one of the methods fails,
|
||||
* the caught exception will be logged as an error, and the remaining
|
||||
* specified {@linkplain TestContext test context}. If one of the methods
|
||||
* fails, the caught exception will be logged as an error, and the remaining
|
||||
* methods will be given a chance to execute. After all methods have
|
||||
* executed, the first caught exception, if any, will be rethrown.
|
||||
* @param testContext the current test context
|
||||
@@ -295,7 +289,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link PlatformTransactionManager transaction manager} to use
|
||||
* Get the {@linkplain PlatformTransactionManager transaction manager} to use
|
||||
* for the supplied {@linkplain TestContext test context} and {@code qualifier}.
|
||||
* <p>Delegates to {@link #getTransactionManager(TestContext)} if the
|
||||
* supplied {@code qualifier} is {@code null} or empty.
|
||||
@@ -334,8 +328,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link PlatformTransactionManager transaction manager} to use
|
||||
* for the supplied {@link TestContext test context}.
|
||||
* Get the {@linkplain PlatformTransactionManager transaction manager}
|
||||
* to use for the supplied {@linkplain TestContext test context}.
|
||||
* <p>The default implementation simply delegates to
|
||||
* {@link TestContextTransactionUtils#retrieveTransactionManager}.
|
||||
* @param testContext the test context for which the transaction manager
|
||||
@@ -343,6 +337,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
* @return the transaction manager to use, or {@code null} if not found
|
||||
* @throws BeansException if an error occurs while retrieving an explicitly
|
||||
* named transaction manager
|
||||
* @throws IllegalStateException if more than one TransactionManagementConfigurer
|
||||
* exists in the ApplicationContext
|
||||
* @see #getTransactionManager(TestContext, String)
|
||||
*/
|
||||
protected PlatformTransactionManager getTransactionManager(TestContext testContext) {
|
||||
@@ -352,21 +348,45 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
|
||||
/**
|
||||
* Determine whether or not to rollback transactions by default for the
|
||||
* supplied {@link TestContext test context}.
|
||||
* supplied {@linkplain TestContext test context}.
|
||||
* <p>Supports {@link Rollback @Rollback} or
|
||||
* {@link TransactionConfiguration @TransactionConfiguration} at the
|
||||
* class-level.
|
||||
* @param testContext the test context for which the default rollback flag
|
||||
* should be retrieved
|
||||
* @return the <em>default rollback</em> flag for the supplied test context
|
||||
* @throws Exception if an error occurs while determining the default rollback flag
|
||||
*/
|
||||
protected final boolean isDefaultRollback(TestContext testContext) throws Exception {
|
||||
return retrieveConfigurationAttributes(testContext).isDefaultRollback();
|
||||
Class<?> testClass = testContext.getTestClass();
|
||||
Rollback rollback = findAnnotation(testClass, Rollback.class);
|
||||
boolean rollbackPresent = (rollback != null);
|
||||
TransactionConfigurationAttributes txConfigAttributes = retrieveConfigurationAttributes(testContext);
|
||||
|
||||
if (rollbackPresent && txConfigAttributes != defaultTxConfigAttributes) {
|
||||
throw new IllegalStateException(String.format("Test class [%s] is annotated with both @Rollback "
|
||||
+ "and @TransactionConfiguration, but only one is permitted.", testClass.getName()));
|
||||
}
|
||||
|
||||
if (rollbackPresent) {
|
||||
boolean defaultRollback = rollback.value();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Retrieved default @Rollback(%s) for test class [%s].", defaultRollback,
|
||||
testClass.getName()));
|
||||
}
|
||||
return defaultRollback;
|
||||
}
|
||||
|
||||
// else
|
||||
return txConfigAttributes.isDefaultRollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not to rollback transactions for the supplied
|
||||
* {@link TestContext test context} by taking into consideration the
|
||||
* {@link #isDefaultRollback(TestContext) default rollback} flag and a
|
||||
* possible method-level override via the {@link Rollback} annotation.
|
||||
* {@linkplain TestContext test context} by taking into consideration the
|
||||
* {@linkplain #isDefaultRollback(TestContext) default rollback} flag and a
|
||||
* possible method-level override via the {@link Rollback @Rollback}
|
||||
* annotation.
|
||||
* @param testContext the test context for which the rollback flag
|
||||
* should be retrieved
|
||||
* @return the <em>rollback</em> flag for the supplied test context
|
||||
@@ -458,8 +478,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied {@link Method current method} is <em>shadowed</em>
|
||||
* by a {@link Method previous method}.
|
||||
* Determine if the supplied {@linkplain Method current method} is
|
||||
* <em>shadowed</em> by a {@linkplain Method previous method}.
|
||||
* <p>Note: This code has been borrowed from
|
||||
* {@link org.junit.internal.runners.TestClass#isShadowed(Method, Method)}.
|
||||
* @param current the current method
|
||||
@@ -482,17 +502,20 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link TransactionConfigurationAttributes} for the
|
||||
* specified {@link Class class} which may optionally declare or inherit
|
||||
* {@link TransactionConfiguration @TransactionConfiguration}. If
|
||||
* {@code @TransactionConfiguration} is not present for the supplied
|
||||
* class, the <em>default values</em> for attributes defined in
|
||||
* {@code @TransactionConfiguration} will be used instead.
|
||||
* Retrieve the {@link TransactionConfigurationAttributes} for the
|
||||
* supplied {@link TestContext} whose {@linkplain Class test class}
|
||||
* may optionally declare or inherit
|
||||
* {@link TransactionConfiguration @TransactionConfiguration}.
|
||||
* <p>If {@code @TransactionConfiguration} is not present for the
|
||||
* supplied {@code TestContext}, a default instance of
|
||||
* {@code TransactionConfigurationAttributes} will be used instead.
|
||||
* @param testContext the test context for which the configuration
|
||||
* attributes should be retrieved
|
||||
* @return the TransactionConfigurationAttributes instance for this listener,
|
||||
* potentially cached
|
||||
* @see TransactionConfigurationAttributes#TransactionConfigurationAttributes()
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) {
|
||||
if (this.configurationAttributes == null) {
|
||||
Class<?> clazz = testContext.getTestClass();
|
||||
@@ -501,18 +524,15 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
TransactionConfiguration.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Retrieved @TransactionConfiguration [%s] for test class [%s].",
|
||||
txConfig, clazz));
|
||||
txConfig, clazz.getName()));
|
||||
}
|
||||
|
||||
if (txConfig == null) {
|
||||
txConfig = defaultTransactionConfiguration;
|
||||
}
|
||||
TransactionConfigurationAttributes configAttributes = (txConfig == null ? defaultTxConfigAttributes
|
||||
: new TransactionConfigurationAttributes(txConfig.transactionManager(), txConfig.defaultRollback()));
|
||||
|
||||
TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes(
|
||||
txConfig.transactionManager(), txConfig.defaultRollback());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Using TransactionConfigurationAttributes %s for class [%s].",
|
||||
configAttributes, clazz));
|
||||
logger.debug(String.format("Using TransactionConfigurationAttributes %s for test class [%s].",
|
||||
configAttributes, clazz.getName()));
|
||||
}
|
||||
this.configurationAttributes = configAttributes;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user