提交 f21fe33e 编写于 作者: S Sam Brannen

Support single, unqualified tx manager in the TCF

TransactionalTestExecutionListener currently requires that the
PlatformTransactionManager bean be named "transactionManager" by
default. Otherwise, the bean name can only be overridden via the
transactionManager attribute of @TransactionConfiguration or the value
attribute of @Transactional.

However, if there is only a single PlatformTransactionManager in the
test's ApplicationContext, then the requirement to specify the exact
name of that bean (or to name it exactly "transactionManager") is often
superfluous.

This commit addresses this issue by refactoring the
TransactionalTestExecutionListener so that it is comparable to the
algorithm for determining the transaction manager used in
TransactionAspectSupport for "production" code. Specifically, the TTEL
now uses the following algorithm to retrieve the transaction manager.

 - look up by type and qualifier from @Transactional
 - else, look up by type and explicit name from
   @TransactionConfiguration
 - else, look up single bean by type
 - else, look up by type and default name from @TransactionConfiguration

Issue: SPR-9645
上级 c0b4e368
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
......@@ -23,17 +23,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
/**
* TransactionConfiguration defines class-level metadata for configuring
* {@code TransactionConfiguration} defines class-level metadata for configuring
* transactional tests.
*
* @author Sam Brannen
* @since 2.5
* @see ContextConfiguration
* @see TransactionalTestExecutionListener
* @see org.springframework.test.context.ContextConfiguration
*/
@Documented
@Inherited
......@@ -42,15 +39,19 @@ import org.springframework.transaction.PlatformTransactionManager;
public @interface TransactionConfiguration {
/**
* The bean name of the {@link PlatformTransactionManager} that is to be
* used to drive transactions. This attribute is not required and only needs
* to be specified explicitly if the bean name of the desired
* PlatformTransactionManager is not "transactionManager".
* <p><b>NOTE:</b> The XML <code>&lt;tx:annotation-driven&gt;</code> element
* also refers to a bean named "transactionManager" by default. If you are
* using both features in combination, make sure to point to the same
* transaction manager bean - here in <code>@TransactionConfiguration</code> and
* also in <code>&lt;tx:annotation-driven transaction-manager="..."&gt;</code>.
* The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
* PlatformTransactionManager} that is to be used to drive transactions.
*
* <p>This attribute is not required and only needs to be specified explicitly
* if there are multiple beans of type {@code PlatformTransactionManager} in
* the test's {@code ApplicationContext} and the bean name of the desired
* {@code PlatformTransactionManager} is not "transactionManager".
*
* <p><b>NOTE:</b> The XML {@code <tx:annotation-driven>} element also refers
* to a bean named "transactionManager" by default. If you are using both
* features in combination, make sure to point to the same transaction manager
* bean - here in {@code @TransactionConfiguration} and also in
* {@code <tx:annotation-driven transaction-manager="...">}.
*/
String transactionManager() default "transactionManager";
......
......@@ -19,7 +19,6 @@ package org.springframework.test.context.transaction;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
......@@ -30,7 +29,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.annotation.NotTransactional;
import org.springframework.test.annotation.Rollback;
......@@ -49,37 +51,35 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* <p>
* <code>TestExecutionListener</code> that provides support for executing
* tests within transactions by using the
* {@code TestExecutionListener} that provides support for executing tests
* within transactions by honoring the
* {@link org.springframework.transaction.annotation.Transactional &#064;Transactional}
* and {@link NotTransactional &#064;NotTransactional} annotations.
* </p>
* <p>
* Changes to the database during a test that is run with &#064;Transactional will be
* run within a transaction that will, by default, be automatically
* and {@link NotTransactional &#064;NotTransactional} annotations. Expects a
* {@link PlatformTransactionManager} bean to be defined in the Spring
* {@link ApplicationContext} for the test.
*
* <p>Changes to the database during a test that is run with {@code @Transactional}
* will be run within a transaction that will, by default, be automatically
* <em>rolled back</em> after completion of the test; whereas, changes to the
* database during a test that is run with &#064;NotTransactional will <strong>not</strong>
* be run within a transaction. Test methods that are not annotated with either
* &#064;Transactional (at the class or method level) or &#064;NotTransactional
* will not be run within a transaction.
* </p>
* <p>
* Transactional commit and rollback behavior can be configured via the
* class-level {@link TransactionConfiguration &#064;TransactionConfiguration} and
* method-level {@link Rollback &#064;Rollback} annotations.
* {@link TransactionConfiguration &#064;TransactionConfiguration} also provides
* configuration of the bean name of the {@link PlatformTransactionManager} that
* is to be used to drive transactions.
* </p>
* <p>
* When executing transactional tests, it is sometimes useful to be able to execute
* certain <em>set up</em> or <em>tear down</em> code outside of a
* transaction. <code>TransactionalTestExecutionListener</code> provides such
* database during a test that is run with {@code @NotTransactional} will
* <strong>not</strong> be run within a transaction. Test methods that are not
* annotated with either {@code @Transactional} (at the class or method level)
* or {@code @NotTransactional} will not be run within a transaction.
*
* <p>Transactional commit and rollback behavior can be configured via the
* class-level {@link TransactionConfiguration @TransactionConfiguration} and
* method-level {@link Rollback @Rollback} annotations. 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 is
* to be used to drive transactions.
*
* <p>When executing transactional tests, it is sometimes useful to be able to
* execute certain <em>set up</em> or <em>tear down</em> code outside of a
* transaction. {@code TransactionalTestExecutionListener} provides such
* support for methods annotated with
* {@link BeforeTransaction &#064;BeforeTransaction} and
* {@link AfterTransaction &#064;AfterTransaction}.
* </p>
* {@link BeforeTransaction @BeforeTransaction} and
* {@link AfterTransaction @AfterTransaction}.
*
* @author Sam Brannen
* @author Juergen Hoeller
......@@ -96,25 +96,33 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
private static final Log logger = LogFactory.getLog(TransactionalTestExecutionListener.class);
private static final String DEFAULT_TRANSACTION_MANAGER_NAME = (String) AnnotationUtils.getDefaultValue(
TransactionConfiguration.class, "transactionManager");
private static final Boolean DEFAULT_DEFAULT_ROLLBACK = (Boolean) AnnotationUtils.getDefaultValue(
TransactionConfiguration.class, "defaultRollback");
protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
private final Map<Method, TransactionContext> transactionContextCache =
Collections.synchronizedMap(new IdentityHashMap<Method, TransactionContext>());
private TransactionConfigurationAttributes configurationAttributes;
private volatile int transactionsStarted = 0;
private final Map<Method, TransactionContext> transactionContextCache = Collections.synchronizedMap(new IdentityHashMap<Method, TransactionContext>());
/**
* If the test method of the supplied {@link TestContext test context} is
* configured to run within a transaction, this method will run
* {@link BeforeTransaction &#064;BeforeTransaction methods} and start a new
* transaction.
* <p>Note that if a {@link BeforeTransaction &#064;BeforeTransaction method} fails,
* remaining {@link BeforeTransaction &#064;BeforeTransaction methods} will not
* <p>Note that if a {@code BeforeTransaction &#064;BeforeTransaction method} fails,
* remaining {@code BeforeTransaction &#064;BeforeTransaction methods} will not
* be invoked, and a transaction will not be started.
* @see org.springframework.transaction.annotation.Transactional
* @see org.springframework.test.annotation.NotTransactional
* @see #getTransactionManager(TestContext, String)
*/
@SuppressWarnings("serial")
@Override
......@@ -131,36 +139,27 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
return;
}
PlatformTransactionManager tm = null;
TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod,
testContext.getTestClass());
TransactionDefinition transactionDefinition = null;
if (transactionAttribute != null) {
transactionDefinition = new DelegatingTransactionAttribute(transactionAttribute) {
transactionAttribute = new DelegatingTransactionAttribute(transactionAttribute) {
public String getName() {
return testMethod.getName();
}
};
}
if (transactionDefinition != null) {
if (logger.isDebugEnabled()) {
logger.debug("Explicit transaction definition [" + transactionDefinition + "] found for test context ["
+ testContext + "]");
logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context "
+ testContext);
}
String qualifier = transactionAttribute.getQualifier();
PlatformTransactionManager tm;
if (StringUtils.hasLength(qualifier)) {
// Use autowire-capable factory in order to support extended
// qualifier matching (only exposed on the internal BeanFactory,
// not on the ApplicationContext).
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
tm = BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
}
else {
tm = getTransactionManager(testContext);
}
TransactionContext txContext = new TransactionContext(tm, transactionDefinition);
tm = getTransactionManager(testContext, transactionAttribute.getQualifier());
}
if (tm != null) {
TransactionContext txContext = new TransactionContext(tm, transactionAttribute);
runBeforeTransactionMethods(testContext);
startNewTransaction(testContext, txContext);
this.transactionContextCache.put(testMethod, txContext);
......@@ -171,7 +170,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* If a transaction is currently active for the test method of the supplied
* {@link TestContext test context}, this method will end the transaction
* and run {@link AfterTransaction &#064;AfterTransaction methods}.
* <p>{@link AfterTransaction &#064;AfterTransaction methods} are guaranteed to be
* <p>{@code AfterTransaction &#064;AfterTransaction methods} are guaranteed to be
* invoked even if an error occurs while ending the transaction.
*/
@Override
......@@ -205,15 +204,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
Collections.reverse(methods);
for (Method method : methods) {
if (logger.isDebugEnabled()) {
logger.debug("Executing @BeforeTransaction method [" + method + "] for test context ["
+ testContext + "]");
logger.debug("Executing @BeforeTransaction method [" + method + "] for test context " + testContext);
}
method.invoke(testContext.getTestInstance());
}
}
catch (InvocationTargetException ex) {
logger.error("Exception encountered while executing @BeforeTransaction methods for test context ["
+ testContext + "]", ex.getTargetException());
logger.error("Exception encountered while executing @BeforeTransaction methods for test context "
+ testContext + ".", ex.getTargetException());
ReflectionUtils.rethrowException(ex.getTargetException());
}
}
......@@ -233,8 +231,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
for (Method method : methods) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Executing @AfterTransaction method [" + method + "] for test context [" + testContext
+ "]");
logger.debug("Executing @AfterTransaction method [" + method + "] for test context " + testContext);
}
method.invoke(testContext.getTestInstance());
}
......@@ -244,14 +241,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
afterTransactionException = targetException;
}
logger.error("Exception encountered while executing @AfterTransaction method [" + method
+ "] for test context [" + testContext + "]", targetException);
+ "] for test context " + testContext, targetException);
}
catch (Exception ex) {
if (afterTransactionException == null) {
afterTransactionException = ex;
}
logger.error("Exception encountered while executing @AfterTransaction method [" + method
+ "] for test context [" + testContext + "]", ex);
+ "] for test context " + testContext, ex);
}
}
......@@ -287,14 +284,50 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
private void endTransaction(TestContext testContext, TransactionContext txContext) throws Exception {
boolean rollback = isRollback(testContext);
if (logger.isTraceEnabled()) {
logger.trace("Ending transaction for test context [" + testContext + "]; transaction manager ["
logger.trace("Ending transaction for test context " + testContext + "; transaction manager ["
+ txContext.transactionStatus + "]; rollback [" + rollback + "]");
}
txContext.endTransaction(rollback);
if (logger.isInfoEnabled()) {
logger.info((rollback ? "Rolled back" : "Committed")
+ " transaction after test execution for test context [" + testContext + "]");
+ " transaction after test execution for test context " + testContext);
}
}
/**
* Get the {@link PlatformTransactionManager transaction manager} to use
* for the supplied {@link TestContext test context} and {@code qualifier}.
* <p>Delegates to {@link #getTransactionManager(TestContext)} if the
* supplied {@code qualifier} is <code>null</code> or empty.
* @param testContext the test context for which the transaction manager
* should be retrieved
* @param qualifier the qualifier for selecting between multiple bean matches;
* may be <code>null</code> or empty
* @return the transaction manager to use, or <code>null</code> if not found
* @throws BeansException if an error occurs while retrieving the transaction manager
* @see #getTransactionManager(TestContext)
*/
protected final PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
// look up by type and qualifier from @Transactional
if (StringUtils.hasText(qualifier)) {
try {
// Use autowire-capable factory in order to support extended qualifier
// matching (only exposed on the internal BeanFactory, not on the
// ApplicationContext).
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
} catch (RuntimeException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Caught exception while retrieving transaction manager for test context " + testContext
+ " and qualifier [" + qualifier + "]", ex);
}
throw ex;
}
}
// else
return getTransactionManager(testContext);
}
/**
......@@ -304,16 +337,34 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* should be retrieved
* @return the transaction manager to use, or <code>null</code> if not found
* @throws BeansException if an error occurs while retrieving the transaction manager
* @see #getTransactionManager(TestContext, String)
*/
protected final PlatformTransactionManager getTransactionManager(TestContext testContext) {
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
String tmName = retrieveConfigurationAttributes(testContext).getTransactionManagerName();
try {
return testContext.getApplicationContext().getBean(tmName, PlatformTransactionManager.class);
}
catch (BeansException ex) {
// look up by type and explicit name from @TransactionConfiguration
if (StringUtils.hasText(tmName) && !DEFAULT_TRANSACTION_MANAGER_NAME.equals(tmName)) {
return bf.getBean(tmName, PlatformTransactionManager.class);
}
// look up single bean by type
if (bf instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) bf;
Map<String, PlatformTransactionManager> beansOfType = BeanFactoryUtils.beansOfTypeIncludingAncestors(
lbf, PlatformTransactionManager.class);
if (beansOfType.size() == 1) {
return beansOfType.values().iterator().next();
}
}
// look up by type and default name from @TransactionConfiguration
return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Caught exception while retrieving transaction manager with bean name [" + tmName
+ "] for test context [" + testContext + "]", ex);
logger.warn("Caught exception while retrieving transaction manager for test context " + testContext, ex);
}
throw ex;
}
......@@ -348,14 +399,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
boolean rollbackOverride = rollbackAnnotation.value();
if (logger.isDebugEnabled()) {
logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback
+ "] for test context [" + testContext + "]");
+ "] for test context " + testContext);
}
rollback = rollbackOverride;
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No method-level @Rollback override: using default rollback [" + rollback
+ "] for test context [" + testContext + "]");
+ "] for test context " + testContext);
}
}
return rollback;
......@@ -451,20 +502,19 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
/**
* Retrieves the {@link TransactionConfigurationAttributes} for the
* specified {@link Class class} which may optionally declare or inherit a
* {@link TransactionConfiguration &#064;TransactionConfiguration}. If a
* {@link TransactionConfiguration} annotation is not present for the
* supplied class, the <em>default values</em> for attributes defined in
* {@link TransactionConfiguration} will be used instead.
* @param clazz the Class object corresponding to the test class for which
* the configuration attributes should be retrieved
* specified {@link Class class} which may optionally declare or inherit
* {@link TransactionConfiguration &#064;TransactionConfiguration}. If
* {@code &#064;TransactionConfiguration} is not present for the supplied
* class, the <em>default values</em> for attributes defined in
* {@code &#064;TransactionConfiguration} will be used instead.
* @param testContext the test context for which the configuration
* attributes should be retrieved
* @return a new TransactionConfigurationAttributes instance
*/
private TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) {
if (this.configurationAttributes == null) {
Class<?> clazz = testContext.getTestClass();
Class<TransactionConfiguration> annotationType = TransactionConfiguration.class;
TransactionConfiguration config = clazz.getAnnotation(annotationType);
TransactionConfiguration config = clazz.getAnnotation(TransactionConfiguration.class);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]");
}
......@@ -476,14 +526,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
defaultRollback = config.defaultRollback();
}
else {
transactionManagerName = (String) AnnotationUtils.getDefaultValue(annotationType, "transactionManager");
defaultRollback = (Boolean) AnnotationUtils.getDefaultValue(annotationType, "defaultRollback");
transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME;
defaultRollback = DEFAULT_DEFAULT_ROLLBACK;
}
TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes(
transactionManagerName, defaultRollback);
if (logger.isDebugEnabled()) {
logger.debug("Retrieved TransactionConfigurationAttributes [" + configAttributes + "] for class ["
logger.debug("Retrieved TransactionConfigurationAttributes " + configAttributes + " for class ["
+ clazz + "]");
}
this.configurationAttributes = configAttributes;
......
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class LookUpNonexistentTxMgrTests {
private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager transactionManager() {
return txManager;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager.clear();
}
@Test
public void lookUpNothing() {
assertEquals(0, txManager.begun);
assertEquals(0, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(0, txManager.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(0, txManager.begun);
assertEquals(0, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(0, txManager.rollbacks);
}
}
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class LookUpTxMgrByTypeAndDefaultNameTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager transactionManager() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Test
public void lookUpByTypeAndDefaultName() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TransactionConfiguration(transactionManager = "txManager1")
public class LookUpTxMgrByTypeAndNameTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager1() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Test
public void lookUpByTypeAndName() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional("txManager1")
public class LookUpTxMgrByTypeAndQualifierAtClassLevelTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager1() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Test
public void lookUpByTypeAndQualifier() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class LookUpTxMgrByTypeAndQualifierAtMethodLevelTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager1() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Transactional("txManager1")
@Test
public void lookUpByTypeAndQualifier() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class LookUpTxMgrByTypeTests {
private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager() {
return txManager;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager.clear();
}
@Test
public void lookUpByType() {
assertEquals(1, txManager.begun);
assertEquals(1, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(0, txManager.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager.begun);
assertEquals(0, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(1, txManager.rollbacks);
}
}
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.transaction;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
/**
* @author Rod Johnson
* @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class CallCountingTransactionManager extends AbstractPlatformTransactionManager {
public TransactionDefinition lastDefinition;
public int begun;
public int commits;
public int rollbacks;
public int inflight;
protected Object doGetTransaction() {
return new Object();
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
this.lastDefinition = definition;
++begun;
++inflight;
}
protected void doCommit(DefaultTransactionStatus status) {
++commits;
--inflight;
}
protected void doRollback(DefaultTransactionStatus status) {
++rollbacks;
--inflight;
}
public void clear() {
begun = commits = rollbacks = inflight = 0;
}
}
......@@ -41,6 +41,10 @@
</logger>
-->
<logger name="org.springframework.test.context.transaction.TransactionalTestExecutionListener">
<level value="warn" />
</logger>
<logger name="org.springframework.test.context">
<level value="warn" />
</logger>
......
......@@ -28,6 +28,7 @@ Changes in version 3.2 M2 (2012-08-xx)
* add exclude patterns for mapped interceptors in MVC namespace and MVC Java config
* support content negotiation options in MVC namespace and MVC Java config
* support named dispatchers in MockServletContext (SPR-9587)
* support single, unqualified tx manager in the TestContext framework (SPR-9645)
Changes in version 3.2 M1 (2012-05-28)
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册