1. Introduction
1.介绍
Spring has excellent support for declarative transaction management throughout application code as well as in integration tests.
Spring对整个应用代码以及集成测试中的声明性事务管理有很好的支持。
However, we may occasionally need fine-grained control over transaction boundaries.
然而,我们偶尔可能需要对事务边界进行细粒度的控制。
In this article, we’ll see how to programmatically interact with automatic transactions set up by Spring in transactional tests.
在这篇文章中,我们将看到如何以编程方式与Spring在事务性测试中设置的自动事务进行交互。
2. Prerequisites
2.先决条件
Let’s assume that we have some integration tests in our Spring application.
让我们假设在我们的Spring应用程序中有一些集成测试。
Specifically, we’re considering tests that interact with a database, for example, check that our persistence layer is behaving correctly.
具体来说,我们正在考虑与数据库交互的测试,例如,检查我们的持久层是否行为正确。
Let’s consider a standard test class – annotated as transactional:
让我们考虑一个标准的测试类–被注解为事务性的。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { HibernateConf.class })
@Transactional
public class HibernateBootstrapIntegrationTest { ... }
In such a test, every test method is wrapped in a transaction, which gets rolled back when the method exits.
在这样的测试中,每个测试方法都被包裹在一个事务中,当方法退出时被回滚。
It’s, of course, also possible to only annotate specific methods. Everything we’ll discuss in this article applies to that scenario as well.
当然,也可以只对特定的方法进行注释。我们将在本文中讨论的一切也适用于这种情况。
3. The TestTransaction Class
3.TestTransaction类
We’ll spend the rest of the article discussing a single class: org.springframework.test.context.transaction.TestTransaction.
我们将在文章的其余部分讨论一个单一的类。org.springframework.test.context.transaction.TestTransaction。
This is a utility class with a few static methods that we can use to interact with transactions in our tests.
这是一个带有一些静态方法的实用类,我们可以用它来与测试中的事务进行交互。
Each method interacts with the only current transaction which is in place during the execution of a test method.
每个方法都与当前唯一的交易互动,该交易在测试方法的执行过程中是存在的。
3.1. Checking the State of the Current Transaction
3.1.检查当前事务的状态
One thing we often do in tests is checking that things are in the state they are supposed to be.
我们在测试中经常做的一件事是检查事情是否处于它们应该有的状态。
Therefore, we might want to check whether there is a currently active transaction:
因此,我们可能想检查当前是否有一个活跃的交易。
assertTrue(TestTransaction.isActive());
Or, we could be interested to check whether the current transaction is flagged for rollback or not:
或者,我们可以对检查当前事务是否被标记为回滚感兴趣。
assertTrue(TestTransaction.isFlaggedForRollback());
If it is, then Spring will roll it back just before it ends, either automatically or programmatically. Otherwise, it’ll commit it just before closing it.
如果是这样,那么Spring会在结束前自动或以编程方式回滚。否则,它就会在关闭前提交。
3.2. Flagging a Transaction for Commit or Rollback
3.2.标记一个事务为提交或回滚
We can change programmatically the policy to commit or to rollback the transaction before closing it:
我们可以通过编程改变策略,在关闭事务之前提交或回滚事务。
TestTransaction.flagForCommit();
TestTransaction.flagForRollback();
Normally, transactions in tests are flagged for rollback when they start. However, if the method has a @Commit annotation, they start flagged for commit instead:
通常情况下,测试中的事务在启动时被标记为回滚。然而,如果该方法有一个@Commit注解,他们开始时就会被标记为提交。
@Test
@Commit
public void testFlagForCommit() {
assertFalse(TestTransaction.isFlaggedForRollback());
}
Note that these methods merely flag the transaction, as their names imply. That is, the transaction isn’t committed or rolled back immediately, but only just before it ends.
请注意,这些方法只是对事务进行标记,正如它们的名字所暗示的那样。也就是说,事务不会立即提交或回滚,而只是在它结束之前。
3.3. Starting and Ending a Transaction
3.3.开始和结束一项事务
To commit or rollback a transaction, we either let the method exit, or we explicitly end it:
要提交或回滚一个事务,我们要么让该方法退出,要么明确地结束它。
TestTransaction.end();
If later on, we want to interact with the database again, we have to start a new transaction:
如果以后我们想再次与数据库互动,我们必须启动一个新的事务。
TestTransaction.start();
Note that the new transaction will be flagged for rollback (or commit) as per the method’s default. In other words, previous calls to flagFor… don’t have any effect on new transactions.
请注意,新的事务将被标记为回滚(或提交),这是该方法的默认值。换句话说,之前对flagFor…的调用对新事务没有任何影响。
4. Some Implementation Details
4.一些实施细节
TestTransaction is nothing magical. We’ll now look at its implementation to learn a little more about transactions in tests with Spring.
TestTransaction没有什么神奇之处。我们现在来看看它的实现,以了解更多关于Spring测试中的事务。
We can see that its few methods simply get access to the current transaction and encapsulate some of its functionality.
我们可以看到,它的几个方法只是获得了对当前事务的访问,并封装了它的一些功能。
4.1. Where Does TestTransaction Get the Current Transaction From?
4.1.TestTransaction从哪里获得当前事务?
Let’s go straight to the code:
让我们直接进入代码。
TransactionContext transactionContext
= TransactionContextHolder.getCurrentTransactionContext();
TransactionContextHolder is just a static wrapper around a ThreadLocal holding a TransactionContext.
TransactionContextHolder只是一个静态包装器,围绕着一个持有TransactionContext的ThreadLocal。
4.2. Who Sets the Thread-Local Context?
4.2.谁来设置线程本地上下文?
If we look at who calls the setCurrentTransactionContext method, we’ll find there’s only one caller: TransactionalTestExecutionListener.beforeTestMethod.
如果我们看一下谁在调用setCurrentTransactionContext方法,我们会发现只有一个调用者。TransactionalTestExecutionListener.beforeTestMethod。
TransactionalTestExecutionListener is the listener that Springs configures automatically on tests that are annotated @Transactional.
TransactionalTestExecutionListener是Spring在注释为@Transactional的测试中自动配置的监听器。
Note that TransactionContext doesn’t hold a reference to any actual transaction; instead, it is merely a façade over the PlatformTransactionManager.
请注意,TransactionContext并不持有对任何实际事务的引用;相反,它只是PlatformTransactionManager上的一个façade。
Yes, this code is heavily layered and abstract. Such are, often, the core parts of the Spring framework.
是的,这段代码具有很强的层次性和抽象性。这样的,往往是Spring框架的核心部分。
It’s interesting to see how, under the complexity, Spring doesn’t do any black magic – just a lot of necessary bookkeeping, plumbing, exception handling and so on.
有趣的是,在复杂性之下,Spring并没有做任何黑魔法–只是做了很多必要的记账、管道、异常处理等等。
5. Conclusions
5.结论
In this quick tutorial, we’ve seen how to interact programmatically with transactions in Spring-based tests.
在这个快速教程中,我们已经看到了如何在基于Spring的测试中以编程方式与事务进行交互。
The implementation of all these examples can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.
所有这些例子的实现都可以在GitHub项目中找到–这是一个Maven项目,所以应该很容易导入并按原样运行。