Browse Source
Historically, Spring's JUnit 3.8 TestCase class hierarchy supported programmatic transaction management of "test-managed transactions" via the protected endTransaction() and startNewTransaction() methods in AbstractTransactionalSpringContextTests. The Spring TestContext Framework (TCF) was introduced in Spring 2.5 to supersede the legacy JUnit 3.8 support classes; however, prior to this commit the TCF has not provided support for programmatically starting or stopping the test-managed transaction. This commit introduces a TestTransaction class in the TCF that provides static utility methods for programmatically interacting with test-managed transactions. Specifically, the following features are supported by TestTransaction and its collaborators. - End the current test-managed transaction. - Start a new test-managed transaction, using the default rollback semantics configured via @TransactionConfiguration and @Rollback. - Flag the current test-managed transaction to be committed. - Flag the current test-managed transaction to be rolled back. Implementation Details: - TransactionContext is now a top-level, package private class. - The existing test transaction management logic has been extracted from TransactionalTestExecutionListener into TransactionContext. - The current TransactionContext is stored in a NamedInheritableThreadLocal that is managed by TransactionContextHolder. - TestTransaction defines the end-user API, interacting with the TransactionContextHolder behind the scenes. - TransactionalTestExecutionListener now delegates to TransactionContext completely for starting and ending transactions. Issue: SPR-5079pull/22439/head
Sam Brannen
11 years ago
8 changed files with 663 additions and 108 deletions
@ -0,0 +1,146 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2014 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.transaction; |
||||||
|
|
||||||
|
import org.springframework.test.context.TestExecutionListeners; |
||||||
|
import org.springframework.transaction.TransactionStatus; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@code TestTransaction} provides a collection of static utility methods for |
||||||
|
* programmatic interaction with <em>test-managed transactions</em>. |
||||||
|
* |
||||||
|
* <p>Test-managed transactions are transactions that are managed by the <em>Spring TestContext Framework</em>. |
||||||
|
* |
||||||
|
* <p>Support for {@code TestTransaction} is automatically available whenever |
||||||
|
* the {@link TransactionalTestExecutionListener} is enabled. Note that the |
||||||
|
* {@code TransactionalTestExecutionListener} is typically enabled by default, |
||||||
|
* but it can also be manually enabled via the |
||||||
|
* {@link TestExecutionListeners @TestExecutionListeners} annotation. |
||||||
|
* |
||||||
|
* @author Sam Brannen |
||||||
|
* @since 4.1 |
||||||
|
* @see TransactionalTestExecutionListener |
||||||
|
*/ |
||||||
|
public class TestTransaction { |
||||||
|
|
||||||
|
/** |
||||||
|
* Determine whether a test-managed transaction is currently <em>active</em>. |
||||||
|
* @return {@code true} if a test-managed transaction is currently active |
||||||
|
* @see #start() |
||||||
|
* @see #end() |
||||||
|
*/ |
||||||
|
public static boolean isActive() { |
||||||
|
TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext(); |
||||||
|
if (transactionContext != null) { |
||||||
|
TransactionStatus transactionStatus = transactionContext.getTransactionStatus(); |
||||||
|
return (transactionStatus != null) && (!transactionStatus.isCompleted()); |
||||||
|
} |
||||||
|
|
||||||
|
// else
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determine whether the current test-managed transaction has been |
||||||
|
* {@linkplain #flagForRollback() flagged for rollback} or |
||||||
|
* {@linkplain #flagForCommit() flagged for commit}. |
||||||
|
* @return {@code true} if the current test-managed transaction is flagged |
||||||
|
* to be rolled back; {@code false} if the current test-managed transaction |
||||||
|
* is flagged to be committed |
||||||
|
* @throws IllegalStateException if a transaction is not active for the |
||||||
|
* current test |
||||||
|
* @see #isActive() |
||||||
|
* @see #flagForRollback() |
||||||
|
* @see #flagForCommit() |
||||||
|
*/ |
||||||
|
public static boolean isFlaggedForRollback() { |
||||||
|
return requireCurrentTransactionContext().isFlaggedForRollback(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag the current test-managed transaction for <em>rollback</em>. |
||||||
|
* <p>Invoking this method will <em>not</em> end the current transaction. |
||||||
|
* Rather, the value of this flag will be used to determine whether or not |
||||||
|
* the current test-managed transaction should be rolled back or committed |
||||||
|
* once it is {@linkplain #end ended}. |
||||||
|
* @throws IllegalStateException if a transaction is not active for the |
||||||
|
* current test |
||||||
|
* @see #isActive() |
||||||
|
* @see #isFlaggedForRollback() |
||||||
|
* @see #start() |
||||||
|
* @see #end() |
||||||
|
*/ |
||||||
|
public static void flagForRollback() { |
||||||
|
setFlaggedForRollback(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag the current test-managed transaction for <em>commit</em>. |
||||||
|
* <p>Invoking this method will <em>not</em> end the current transaction. |
||||||
|
* Rather, the value of this flag will be used to determine whether or not |
||||||
|
* the current test-managed transaction should be rolled back or committed |
||||||
|
* once it is {@linkplain #end ended}. |
||||||
|
* @throws IllegalStateException if a transaction is not active for the |
||||||
|
* current test |
||||||
|
* @see #isActive() |
||||||
|
* @see #isFlaggedForRollback() |
||||||
|
* @see #start() |
||||||
|
* @see #end() |
||||||
|
*/ |
||||||
|
public static void flagForCommit() { |
||||||
|
setFlaggedForRollback(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Start a new test-managed transaction. |
||||||
|
* <p>Only call this method if {@link #end} has been called or if no |
||||||
|
* transaction has been previously started. |
||||||
|
* @throws IllegalStateException if the transaction context could not be |
||||||
|
* retrieved or if a transaction is already active for the current test |
||||||
|
* @see #isActive() |
||||||
|
* @see #end() |
||||||
|
*/ |
||||||
|
public static void start() { |
||||||
|
requireCurrentTransactionContext().startTransaction(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Immediately force a <em>commit</em> or <em>rollback</em> of the current |
||||||
|
* test-managed transaction, according to the {@linkplain #isFlaggedForRollback |
||||||
|
* rollback flag}. |
||||||
|
* @throws IllegalStateException if the transaction context could not be |
||||||
|
* retrieved or if a transaction is not active for the current test |
||||||
|
* @see #isActive() |
||||||
|
* @see #start() |
||||||
|
*/ |
||||||
|
public static void end() { |
||||||
|
requireCurrentTransactionContext().endTransaction(); |
||||||
|
} |
||||||
|
|
||||||
|
private static TransactionContext requireCurrentTransactionContext() { |
||||||
|
TransactionContext txContext = TransactionContextHolder.getCurrentTransactionContext(); |
||||||
|
if (txContext == null) { |
||||||
|
throw new IllegalStateException("TransactionContext is not active"); |
||||||
|
} |
||||||
|
return txContext; |
||||||
|
} |
||||||
|
|
||||||
|
private static void setFlaggedForRollback(boolean flag) { |
||||||
|
requireCurrentTransactionContext().setFlaggedForRollback(flag); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2014 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.transaction; |
||||||
|
|
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
import org.springframework.test.context.TestContext; |
||||||
|
import org.springframework.transaction.PlatformTransactionManager; |
||||||
|
import org.springframework.transaction.TransactionDefinition; |
||||||
|
import org.springframework.transaction.TransactionException; |
||||||
|
import org.springframework.transaction.TransactionStatus; |
||||||
|
|
||||||
|
/** |
||||||
|
* Transaction context for a specific {@link TestContext}. |
||||||
|
* |
||||||
|
* @author Sam Brannen |
||||||
|
* @author Juergen Hoeller |
||||||
|
* @since 4.1 |
||||||
|
* @see org.springframework.transaction.annotation.Transactional |
||||||
|
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener |
||||||
|
*/ |
||||||
|
class TransactionContext { |
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(TransactionContext.class); |
||||||
|
|
||||||
|
private final TestContext testContext; |
||||||
|
|
||||||
|
private final TransactionDefinition transactionDefinition; |
||||||
|
|
||||||
|
private final PlatformTransactionManager transactionManager; |
||||||
|
|
||||||
|
private final boolean defaultRollback; |
||||||
|
|
||||||
|
private boolean flaggedForRollback; |
||||||
|
|
||||||
|
private TransactionStatus transactionStatus; |
||||||
|
|
||||||
|
private volatile int transactionsStarted = 0; |
||||||
|
|
||||||
|
|
||||||
|
TransactionContext(TestContext testContext, PlatformTransactionManager transactionManager, |
||||||
|
TransactionDefinition transactionDefinition, boolean defaultRollback) { |
||||||
|
this.testContext = testContext; |
||||||
|
this.transactionManager = transactionManager; |
||||||
|
this.transactionDefinition = transactionDefinition; |
||||||
|
this.defaultRollback = defaultRollback; |
||||||
|
this.flaggedForRollback = defaultRollback; |
||||||
|
} |
||||||
|
|
||||||
|
TransactionStatus getTransactionStatus() { |
||||||
|
return this.transactionStatus; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Has the current transaction been flagged for rollback? |
||||||
|
* <p>In other words, should we roll back or commit the current transaction |
||||||
|
* upon completion of the current test? |
||||||
|
*/ |
||||||
|
boolean isFlaggedForRollback() { |
||||||
|
return this.flaggedForRollback; |
||||||
|
} |
||||||
|
|
||||||
|
void setFlaggedForRollback(boolean flaggedForRollback) { |
||||||
|
if (this.transactionStatus == null) { |
||||||
|
throw new IllegalStateException(String.format( |
||||||
|
"Failed to set rollback flag for test context %s: transaction does not exist.", this.testContext)); |
||||||
|
} |
||||||
|
this.flaggedForRollback = flaggedForRollback; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Start a new transaction for the configured {@linkplain #getTestContext test context}. |
||||||
|
* <p>Only call this method if {@link #endTransaction} has been called or if no |
||||||
|
* transaction has been previously started. |
||||||
|
* @throws TransactionException if starting the transaction fails |
||||||
|
*/ |
||||||
|
void startTransaction() { |
||||||
|
if (this.transactionStatus != null) { |
||||||
|
throw new IllegalStateException( |
||||||
|
"Cannot start a new transaction without ending the existing transaction first."); |
||||||
|
} |
||||||
|
this.flaggedForRollback = this.defaultRollback; |
||||||
|
this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); |
||||||
|
++this.transactionsStarted; |
||||||
|
if (logger.isInfoEnabled()) { |
||||||
|
logger.info(String.format( |
||||||
|
"Began transaction (%s) for test context %s; transaction manager [%s]; rollback [%s]", |
||||||
|
this.transactionsStarted, this.testContext, this.transactionManager, flaggedForRollback)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Immediately force a <em>commit</em> or <em>rollback</em> of the transaction |
||||||
|
* for the configured {@linkplain #getTestContext test context}, according to |
||||||
|
* the {@linkplain #isFlaggedForRollback rollback flag}. |
||||||
|
*/ |
||||||
|
void endTransaction() { |
||||||
|
if (logger.isTraceEnabled()) { |
||||||
|
logger.trace(String.format( |
||||||
|
"Ending transaction for test context %s; transaction status [%s]; rollback [%s]", this.testContext, |
||||||
|
this.transactionStatus, flaggedForRollback)); |
||||||
|
} |
||||||
|
if (this.transactionStatus == null) { |
||||||
|
throw new IllegalStateException(String.format( |
||||||
|
"Failed to end transaction for test context %s: transaction does not exist.", this.testContext)); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
if (flaggedForRollback) { |
||||||
|
this.transactionManager.rollback(this.transactionStatus); |
||||||
|
} |
||||||
|
else { |
||||||
|
this.transactionManager.commit(this.transactionStatus); |
||||||
|
} |
||||||
|
} |
||||||
|
finally { |
||||||
|
this.transactionStatus = null; |
||||||
|
} |
||||||
|
|
||||||
|
if (logger.isInfoEnabled()) { |
||||||
|
logger.info(String.format("%s transaction after test execution for test context %s.", |
||||||
|
(flaggedForRollback ? "Rolled back" : "Committed"), this.testContext)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2014 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.transaction; |
||||||
|
|
||||||
|
import org.springframework.core.NamedInheritableThreadLocal; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link InheritableThreadLocal}-based holder for the current {@link TransactionContext}. |
||||||
|
* |
||||||
|
* @author Sam Brannen |
||||||
|
* @since 4.1 |
||||||
|
*/ |
||||||
|
class TransactionContextHolder { |
||||||
|
|
||||||
|
private static final ThreadLocal<TransactionContext> currentTransactionContext = new NamedInheritableThreadLocal<TransactionContext>( |
||||||
|
"Test Transaction Context"); |
||||||
|
|
||||||
|
|
||||||
|
static TransactionContext getCurrentTransactionContext() { |
||||||
|
return currentTransactionContext.get(); |
||||||
|
} |
||||||
|
|
||||||
|
static void setCurrentTransactionContext(TransactionContext transactionContext) { |
||||||
|
currentTransactionContext.set(transactionContext); |
||||||
|
} |
||||||
|
|
||||||
|
static TransactionContext removeCurrentTransactionContext() { |
||||||
|
synchronized (currentTransactionContext) { |
||||||
|
TransactionContext transactionContext = currentTransactionContext.get(); |
||||||
|
currentTransactionContext.remove(); |
||||||
|
return transactionContext; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,284 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2014 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.transaction.programmatic; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import javax.sql.DataSource; |
||||||
|
|
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.TestName; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager; |
||||||
|
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; |
||||||
|
import org.springframework.test.annotation.Rollback; |
||||||
|
import org.springframework.test.context.ContextConfiguration; |
||||||
|
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; |
||||||
|
import org.springframework.test.context.transaction.AfterTransaction; |
||||||
|
import org.springframework.test.context.transaction.BeforeTransaction; |
||||||
|
import org.springframework.test.context.transaction.TestTransaction; |
||||||
|
import org.springframework.transaction.PlatformTransactionManager; |
||||||
|
import org.springframework.transaction.annotation.Propagation; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
import static org.springframework.test.transaction.TransactionTestUtils.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Integration tests that verify support for programmatic transaction management |
||||||
|
* within the <em>Spring TestContext Framework</em>. |
||||||
|
* |
||||||
|
* @author Sam Brannen |
||||||
|
* @since 4.1 |
||||||
|
*/ |
||||||
|
@ContextConfiguration |
||||||
|
public class ProgrammaticTxMgmtTests extends AbstractTransactionalJUnit4SpringContextTests { |
||||||
|
|
||||||
|
@Rule |
||||||
|
public TestName testName = new TestName(); |
||||||
|
|
||||||
|
|
||||||
|
@BeforeTransaction |
||||||
|
public void beforeTransaction() { |
||||||
|
deleteFromTables("user"); |
||||||
|
executeSqlScript("classpath:/org/springframework/test/context/jdbc/data.sql", false); |
||||||
|
} |
||||||
|
|
||||||
|
@AfterTransaction |
||||||
|
public void afterTransaction() { |
||||||
|
String method = testName.getMethodName(); |
||||||
|
switch (method) { |
||||||
|
case "commitTxAndStartNewTx": { |
||||||
|
assertUsers("Dogbert"); |
||||||
|
break; |
||||||
|
} |
||||||
|
case "commitTxButDoNotStartNewTx": { |
||||||
|
assertUsers("Dogbert"); |
||||||
|
break; |
||||||
|
} |
||||||
|
case "rollbackTxAndStartNewTx": { |
||||||
|
assertUsers("Dilbert"); |
||||||
|
break; |
||||||
|
} |
||||||
|
case "rollbackTxButDoNotStartNewTx": { |
||||||
|
assertUsers("Dilbert"); |
||||||
|
break; |
||||||
|
} |
||||||
|
case "rollbackTxAndStartNewTxWithDefaultCommitSemantics": { |
||||||
|
assertUsers("Dilbert", "Dogbert"); |
||||||
|
break; |
||||||
|
} |
||||||
|
case "startTxWithExistingTransaction": { |
||||||
|
assertUsers("Dilbert"); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
fail("missing 'after transaction' assertion for test method: " + method); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED) |
||||||
|
public void isActiveWithNonExistentTransactionContext() { |
||||||
|
assertFalse(TestTransaction.isActive()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class) |
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED) |
||||||
|
public void flagForRollbackWithNonExistentTransactionContext() { |
||||||
|
TestTransaction.flagForRollback(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class) |
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED) |
||||||
|
public void flagForCommitWithNonExistentTransactionContext() { |
||||||
|
TestTransaction.flagForCommit(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class) |
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED) |
||||||
|
public void isFlaggedForRollbackWithNonExistentTransactionContext() { |
||||||
|
TestTransaction.isFlaggedForRollback(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class) |
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED) |
||||||
|
public void startTxWithNonExistentTransactionContext() { |
||||||
|
TestTransaction.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class) |
||||||
|
public void startTxWithExistingTransaction() { |
||||||
|
TestTransaction.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class) |
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED) |
||||||
|
public void endTxWithNonExistentTransactionContext() { |
||||||
|
TestTransaction.end(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void commitTxAndStartNewTx() { |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
deleteFromTables("user"); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
// Commit
|
||||||
|
TestTransaction.flagForCommit(); |
||||||
|
assertFalse(TestTransaction.isFlaggedForRollback()); |
||||||
|
TestTransaction.end(); |
||||||
|
assertInTransaction(false); |
||||||
|
assertFalse(TestTransaction.isActive()); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
executeSqlScript("classpath:/org/springframework/test/context/jdbc/data-add-dogbert.sql", false); |
||||||
|
assertUsers("Dogbert"); |
||||||
|
|
||||||
|
TestTransaction.start(); |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void commitTxButDoNotStartNewTx() { |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
deleteFromTables("user"); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
// Commit
|
||||||
|
TestTransaction.flagForCommit(); |
||||||
|
assertFalse(TestTransaction.isFlaggedForRollback()); |
||||||
|
TestTransaction.end(); |
||||||
|
assertFalse(TestTransaction.isActive()); |
||||||
|
assertInTransaction(false); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
executeSqlScript("classpath:/org/springframework/test/context/jdbc/data-add-dogbert.sql", false); |
||||||
|
assertUsers("Dogbert"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void rollbackTxAndStartNewTx() { |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
deleteFromTables("user"); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
// Rollback (automatically)
|
||||||
|
assertTrue(TestTransaction.isFlaggedForRollback()); |
||||||
|
TestTransaction.end(); |
||||||
|
assertFalse(TestTransaction.isActive()); |
||||||
|
assertInTransaction(false); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
|
||||||
|
// Start new transaction with default rollback semantics
|
||||||
|
TestTransaction.start(); |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isFlaggedForRollback()); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
|
||||||
|
executeSqlScript("classpath:/org/springframework/test/context/jdbc/data-add-dogbert.sql", false); |
||||||
|
assertUsers("Dilbert", "Dogbert"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void rollbackTxButDoNotStartNewTx() { |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
deleteFromTables("user"); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
// Rollback (automatically)
|
||||||
|
assertTrue(TestTransaction.isFlaggedForRollback()); |
||||||
|
TestTransaction.end(); |
||||||
|
assertFalse(TestTransaction.isActive()); |
||||||
|
assertInTransaction(false); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
@Rollback(false) |
||||||
|
public void rollbackTxAndStartNewTxWithDefaultCommitSemantics() { |
||||||
|
assertInTransaction(true); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
deleteFromTables("user"); |
||||||
|
assertUsers(); |
||||||
|
|
||||||
|
// Rollback
|
||||||
|
TestTransaction.flagForRollback(); |
||||||
|
assertTrue(TestTransaction.isFlaggedForRollback()); |
||||||
|
TestTransaction.end(); |
||||||
|
assertFalse(TestTransaction.isActive()); |
||||||
|
assertInTransaction(false); |
||||||
|
assertUsers("Dilbert"); |
||||||
|
|
||||||
|
// Start new transaction with default commit semantics
|
||||||
|
TestTransaction.start(); |
||||||
|
assertInTransaction(true); |
||||||
|
assertFalse(TestTransaction.isFlaggedForRollback()); |
||||||
|
assertTrue(TestTransaction.isActive()); |
||||||
|
|
||||||
|
executeSqlScript("classpath:/org/springframework/test/context/jdbc/data-add-dogbert.sql", false); |
||||||
|
assertUsers("Dilbert", "Dogbert"); |
||||||
|
} |
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void assertUsers(String... users) { |
||||||
|
List<Map<String, Object>> results = jdbcTemplate.queryForList("select name from user"); |
||||||
|
List<String> names = new ArrayList<String>(); |
||||||
|
for (Map<String, Object> map : results) { |
||||||
|
names.add((String) map.get("name")); |
||||||
|
} |
||||||
|
assertEquals(Arrays.asList(users), names); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Configuration |
||||||
|
static class Config { |
||||||
|
|
||||||
|
@Bean |
||||||
|
public PlatformTransactionManager transactionManager() { |
||||||
|
return new DataSourceTransactionManager(dataSource()); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public DataSource dataSource() { |
||||||
|
return new EmbeddedDatabaseBuilder()//
|
||||||
|
.setName("programmatic-tx-mgmt-test-db")//
|
||||||
|
.addScript("classpath:/org/springframework/test/context/jdbc/schema.sql") //
|
||||||
|
.build(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue