Transaction Management

在 TestContext 框架中,事务由 TransactionalTestExecutionListener 管理,该监听器默认情况下已配置,即使你没有在你测试类上显式申报 @TestExecutionListeners。但是,要启用对事务的支持,你必须在使用 @ContextConfiguration 语义加载的 ApplicationContext 中配置一个 PlatformTransactionManager bean(稍后将提供更多详细信息)。此外,你必须为你的测试在类级或方法级上申报 Spring 的 @Transactional 注解。

In the TestContext framework, transactions are managed by the TransactionalTestExecutionListener, which is configured by default, even if you do not explicitly declare @TestExecutionListeners on your test class. To enable support for transactions, however, you must configure a PlatformTransactionManager bean in the ApplicationContext that is loaded with @ContextConfiguration semantics (further details are provided later). In addition, you must declare Spring’s @Transactional annotation either at the class or the method level for your tests.

Test-managed Transactions

测试管理的事务是通过使用 TransactionalTestExecutionListener 声明式管理的事务,或通过使用 TestTransaction 程序化管理的事务(稍后描述)。您不应将此类事务与 Spring 管理的事务(直接由 Spring 在为测试加载的 ApplicationContext 中管理的事务)或应用程序管理的事务(在测试调用的应用程序代码中通过编程管理的事务)混淆。Spring 管理的事务和应用程序管理的事务通常参与测试管理的事务。但是,如果 Spring 管理的事务或应用程序管理的事务配置的传播类型与 REQUIREDSUPPORTS 以外的任何其他传播类型,则应小心(请参阅有关 transaction propagation 的讨论以了解详情)。

Test-managed transactions are transactions that are managed declaratively by using the TransactionalTestExecutionListener or programmatically by using TestTransaction (described later). You should not confuse such transactions with Spring-managed transactions (those managed directly by Spring within the ApplicationContext loaded for tests) or application-managed transactions (those managed programmatically within application code that is invoked by tests). Spring-managed and application-managed transactions typically participate in test-managed transactions. However, you should use caution if Spring-managed or application-managed transactions are configured with any propagation type other than REQUIRED or SUPPORTS (see the discussion on transaction propagation for details).

Example 1. Preemptive timeouts and test-managed transactions

在将测试框架的任何形式的抢占超时与 Spring 的测试管理事务结合使用时,必须小心。

Caution must be taken when using any form of preemptive timeouts from a testing framework in conjunction with Spring’s test-managed transactions.

具体来说,Spring 的测试支持在调用当前测试方法 之前 将事务状态绑定到当前线程(通过 java.lang.ThreadLocal 变量)。如果测试框架在一个新的线程中调用当前测试方法以支持抢占超时,则在当前测试方法中执行的任何操作 不会 在测试管理的事务中被调用。因此,任何此类操作的结果都不会随测试管理的事务一起回滚。相反,这些操作将提交到持久存储 中——例如关系数据库 ——即使 Spring 正确回滚了测试管理的事务也是如此。

Specifically, Spring’s testing support binds transaction state to the current thread (via a java.lang.ThreadLocal variable) before the current test method is invoked. If a testing framework invokes the current test method in a new thread in order to support a preemptive timeout, any actions performed within the current test method will not be invoked within the test-managed transaction. Consequently, the result of any such actions will not be rolled back with the test-managed transaction. On the contrary, such actions will be committed to the persistent store — for example, a relational database — even though the test-managed transaction is properly rolled back by Spring.

可能发生这种情况的场景包括但不限于以下内容。

Situations in which this can occur include but are not limited to the following.

  • JUnit 4’s @Test(timeout = …​) support and TimeOut rule

  • JUnit Jupiter’s assertTimeoutPreemptively(…​) methods in the org.junit.jupiter.api.Assertions class

  • TestNG’s @Test(timeOut = …​) support

Enabling and Disabling Transactions

使用 @Transactional 对测试方法进行注释会导致测试在事务中运行,该事务在默认情况下会在测试完成后自动回滚。如果使用 @Transactional 对一个测试类进行注释,那么该类层次结构中的每个测试方法都在事务中运行。没有使用 @Transactional(在类级或方法级)对进行注释的测试方法不会在事务中运行。请注意, @Transactional 不支持测试生命周期方法——例如,使用 JUnit Jupiter 的 @BeforeAll@BeforeEach 等进行注释的方法。此外,用 @Transactional 进行注释但将 propagation 属性设置为 NOT_SUPPORTEDNEVER 的测试不会在事务中运行。

Annotating a test method with @Transactional causes the test to be run within a transaction that is, by default, automatically rolled back after completion of the test. If a test class is annotated with @Transactional, each test method within that class hierarchy runs within a transaction. Test methods that are not annotated with @Transactional (at the class or method level) are not run within a transaction. Note that @Transactional is not supported on test lifecycle methods — for example, methods annotated with JUnit Jupiter’s @BeforeAll, @BeforeEach, etc. Furthermore, tests that are annotated with @Transactional but have the propagation attribute set to NOT_SUPPORTED or NEVER are not run within a transaction.

Table 1. @Transactional attribute support
Attribute Supported for test-managed transactions

value and transactionManager

yes

propagation

only Propagation.NOT_SUPPORTED and Propagation.NEVER are supported

isolation

no

timeout

no

readOnly

no

rollbackFor and rollbackForClassName

no: use TestTransaction.flagForRollback() instead

noRollbackFor and noRollbackForClassName

no: use TestTransaction.flagForCommit() instead

方法级生命周期方法——例如,使用 JUnit Jupiter 的 @BeforeEach@AfterEach 进行注释的方法——在测试管理的事务中运行。另一方面,套件级和类级生命周期方法——例如,使用 JUnit Jupiter 的 @BeforeAll@AfterAll 进行注释的方法,以及使用 TestNG 的 @BeforeSuite@AfterSuite@BeforeClass@AfterClass 进行注释的方法—— 不会 在测试管理的事务中运行。

Method-level lifecycle methods — for example, methods annotated with JUnit Jupiter’s @BeforeEach or @AfterEach — are run within a test-managed transaction. On the other hand, suite-level and class-level lifecycle methods — for example, methods annotated with JUnit Jupiter’s @BeforeAll or @AfterAll and methods annotated with TestNG’s @BeforeSuite, @AfterSuite, @BeforeClass, or @AfterClass — are not run within a test-managed transaction.

如果你需要在套件级或类级生命周期方法中运行事务中的代码,你可能希望将相应的 PlatformTransactionManager 注入到你的测试类中,然后将其与 TransactionTemplate 一起用于编程方式事务管理。

If you need to run code in a suite-level or class-level lifecycle method within a transaction, you may wish to inject a corresponding PlatformTransactionManager into your test class and then use that with a TransactionTemplate for programmatic transaction management.

请注意,AbstractTransactionalJUnit4SpringContextTestsAbstractTransactionalTestNGSpringContextTests已预先配置为在类级别提供事务支持。

Note that AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests are preconfigured for transactional support at the class level.

以下示例演示了为基于 Hibernate 的 UserRepository 编写集成测试的常见场景:

The following example demonstrates a common scenario for writing an integration test for a Hibernate-based UserRepository:

  • Java

  • Kotlin

@SpringJUnitConfig(TestConfig.class)
@Transactional
class HibernateUserRepositoryTests {

	@Autowired
	HibernateUserRepository repository;

	@Autowired
	SessionFactory sessionFactory;

	JdbcTemplate jdbcTemplate;

	@Autowired
	void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	@Test
	void createUser() {
		// track initial state in test database:
		final int count = countRowsInTable("user");

		User user = new User(...);
		repository.save(user);

		// Manual flush is required to avoid false positive in test
		sessionFactory.getCurrentSession().flush();
		assertNumUsers(count + 1);
	}

	private int countRowsInTable(String tableName) {
		return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
	}

	private void assertNumUsers(int expected) {
		assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
	}
}
@SpringJUnitConfig(TestConfig::class)
@Transactional
class HibernateUserRepositoryTests {

	@Autowired
	lateinit var repository: HibernateUserRepository

	@Autowired
	lateinit var sessionFactory: SessionFactory

	lateinit var jdbcTemplate: JdbcTemplate

	@Autowired
	fun setDataSource(dataSource: DataSource) {
		this.jdbcTemplate = JdbcTemplate(dataSource)
	}

	@Test
	fun createUser() {
		// track initial state in test database:
		val count = countRowsInTable("user")

		val user = User()
		repository.save(user)

		// Manual flush is required to avoid false positive in test
		sessionFactory.getCurrentSession().flush()
		assertNumUsers(count + 1)
	}

	private fun countRowsInTable(tableName: String): Int {
		return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName)
	}

	private fun assertNumUsers(expected: Int) {
		assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"))
	}
}

Transaction Rollback and Commit Behavior 中所述,无需在运行 createUser() 方法后清理数据库,因为对数据库所做的任何更改都将自动由 TransactionalTestExecutionListener 回滚。

As explained in Transaction Rollback and Commit Behavior, there is no need to clean up the database after the createUser() method runs, since any changes made to the database are automatically rolled back by the TransactionalTestExecutionListener.

Transaction Rollback and Commit Behavior

默认情况下,测试事务将在测试完成后自动回滚;但是,可以通过 @Commit@Rollback 注释以声明方式配置事务提交和回滚行为。有关更多详细信息,请参阅 annotation support 部分中的相应条目。

By default, test transactions will be automatically rolled back after completion of the test; however, transactional commit and rollback behavior can be configured declaratively via the @Commit and @Rollback annotations. See the corresponding entries in the annotation support section for further details.

Programmatic Transaction Management

你可以使用 TestTransaction 中的静态方法以编程方式与测试管理的事务进行交互。例如,你可以在测试方法、方法之前和方法之后使用 TestTransaction 来启动或结束当前测试管理的事务,或为回滚或提交配置当前测试管理的事务。只要启用了 TransactionalTestExecutionListener,就会自动提供对 TestTransaction 的支持。

You can interact with test-managed transactions programmatically by using the static methods in TestTransaction. For example, you can use TestTransaction within test methods, before methods, and after methods to start or end the current test-managed transaction or to configure the current test-managed transaction for rollback or commit. Support for TestTransaction is automatically available whenever the TransactionalTestExecutionListener is enabled.

以下示例演示了 TestTransaction 的一些功能。有关更多详细信息,请参阅https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/transaction/TestTransaction.html[TestTransaction] 中的 javadoc。

The following example demonstrates some of the features of TestTransaction. See the javadoc for TestTransaction for further details.

  • Java

  • Kotlin

@ContextConfiguration(classes = TestConfig.class)
public class ProgrammaticTransactionManagementTests extends
		AbstractTransactionalJUnit4SpringContextTests {

	@Test
	public void transactionalTest() {
		// assert initial state in test database:
		assertNumUsers(2);

		deleteFromTables("user");

		// changes to the database will be committed!
		TestTransaction.flagForCommit();
		TestTransaction.end();
		assertFalse(TestTransaction.isActive());
		assertNumUsers(0);

		TestTransaction.start();
		// perform other actions against the database that will
		// be automatically rolled back after the test completes...
	}

	protected void assertNumUsers(int expected) {
		assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
	}
}
@ContextConfiguration(classes = [TestConfig::class])
class ProgrammaticTransactionManagementTests : AbstractTransactionalJUnit4SpringContextTests() {

	@Test
	fun transactionalTest() {
		// assert initial state in test database:
		assertNumUsers(2)

		deleteFromTables("user")

		// changes to the database will be committed!
		TestTransaction.flagForCommit()
		TestTransaction.end()
		assertFalse(TestTransaction.isActive())
		assertNumUsers(0)

		TestTransaction.start()
		// perform other actions against the database that will
		// be automatically rolled back after the test completes...
	}

	protected fun assertNumUsers(expected: Int) {
		assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"))
	}
}

Running Code Outside of a Transaction

有时,您可能需要在事务性测试方法之前或之后运行某些代码,但位于事务性上下文之外——例如,为了在运行测试之前验证初始数据库状态或在测试运行之后验证预期的事务提交行为(如果将测试配置为提交事务)。TransactionalTestExecutionListener 支持 @BeforeTransaction@AfterTransaction 注释,用于支持上述场景。您可以使用这些注释之一在测试类中的任何 void 方法或者在测试接口中的任何 void 默认方法上添加注释,而 TransactionalTestExecutionListener 确保您的事务前方法或事务后方法在适当的时间运行。

Occasionally, you may need to run certain code before or after a transactional test method but outside the transactional context — for example, to verify the initial database state prior to running your test or to verify expected transactional commit behavior after your test runs (if the test was configured to commit the transaction). TransactionalTestExecutionListener supports the @BeforeTransaction and @AfterTransaction annotations for exactly such scenarios. You can annotate any void method in a test class or any void default method in a test interface with one of these annotations, and the TransactionalTestExecutionListener ensures that your before-transaction method or after-transaction method runs at the appropriate time.

一般来说,@BeforeTransaction@AfterTransaction 方法不能接受任何参数。

Generally speaking, @BeforeTransaction and @AfterTransaction methods must not accept any arguments.

但是,从 Spring Framework 6.1 开始,对于使用SpringExtension和 JUnit Jupiter 的测试,@BeforeTransaction@AfterTransaction 方法可以选择接受参数,这些参数将由任何已注册的 JUnit ParameterResolver 扩展(例如 SpringExtension)解析。这意味着可以向`@BeforeTransaction` 和 @AfterTransaction 方法提供 JUnit 特定的参数,如`TestInfo` 或测试的 ApplicationContext 中的 bean,如下面的示例中所示。

However, as of Spring Framework 6.1, for tests using the SpringExtension with JUnit Jupiter, @BeforeTransaction and @AfterTransaction methods may optionally accept arguments which will be resolved by any registered JUnit ParameterResolver extension such as the SpringExtension. This means that JUnit-specific arguments like TestInfo or beans from the test’s ApplicationContext may be provided to @BeforeTransaction and @AfterTransaction methods, as demonstrated in the following example.

Java
@BeforeTransaction
void verifyInitialDatabaseState(@Autowired DataSource dataSource) {
	// Use the DataSource to verify the initial state before a transaction is started
}
Kotlin
@BeforeTransaction
fun verifyInitialDatabaseState(@Autowired dataSource: DataSource) {
	// Use the DataSource to verify the initial state before a transaction is started
}

任何前置方法(例如使用 JUnit Jupiter 的 @BeforeEach 注释的方法)以及任何后置方法(例如使用 JUnit Jupiter 的 `@AfterEach`注释的方法)针对事务性测试方法运行时会运行在测试管理的事务中。

Any before methods (such as methods annotated with JUnit Jupiter’s @BeforeEach) and any after methods (such as methods annotated with JUnit Jupiter’s @AfterEach) are run within the test-managed transaction for a transactional test method.

类似地,使用 @BeforeTransaction@AfterTransaction 注释的方法只会针对事务性测试方法运行。

Similarly, methods annotated with @BeforeTransaction or @AfterTransaction are only run for transactional test methods.

Configuring a Transaction Manager

TransactionalTestExecutionListener 预期 ApplicationContext 用于测试的 Spring 中定义了一个 PlatformTransactionManager bean。如果测试的 ApplicationContext 中有多个 PlatformTransactionManager 实例,则可以使用 @Transactional("myTxMgr")@Transactional(transactionManager = "myTxMgr") 声明限定符,或者可以通过一个 @Configuration 类来实现 TransactionManagementConfigurer。查阅 TestContextTransactionUtils.retrieveTransactionManager() 的 javadoc 了解在测试的 ApplicationContext 中查找事务管理器的算法的详细信息。

TransactionalTestExecutionListener expects a PlatformTransactionManager bean to be defined in the Spring ApplicationContext for the test. If there are multiple instances of PlatformTransactionManager within the test’s ApplicationContext, you can declare a qualifier by using @Transactional("myTxMgr") or @Transactional(transactionManager = "myTxMgr"), or TransactionManagementConfigurer can be implemented by an @Configuration class. Consult the javadoc for TestContextTransactionUtils.retrieveTransactionManager() for details on the algorithm used to look up a transaction manager in the test’s ApplicationContext.

Demonstration of All Transaction-related Annotations

以下基于 JUnit Jupiter 的示例显示了一个虚构的集成测试场景,该场景突出了所有与事务相关的注释。该示例的目的是演示这些注释如何使用,而不是演示最佳实践。有关详细信息和配置示例,请参阅annotation support 部分。Transaction management for @Sql 包含一个附加示例,它使用 @Sql 来执行声明性 SQL 脚本并带有默认事务回滚语义。以下示例显示了相关的注释:

The following JUnit Jupiter based example displays a fictitious integration testing scenario that highlights all transaction-related annotations. The example is not intended to demonstrate best practices but rather to demonstrate how these annotations can be used. See the annotation support section for further information and configuration examples. Transaction management for @Sql contains an additional example that uses @Sql for declarative SQL script execution with default transaction rollback semantics. The following example shows the relevant annotations:

  • Java

  • Kotlin

@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {

	@BeforeTransaction
	void verifyInitialDatabaseState() {
		// logic to verify the initial state before a transaction is started
	}

	@BeforeEach
	void setUpTestDataWithinTransaction() {
		// set up test data within the transaction
	}

	@Test
	// overrides the class-level @Commit setting
	@Rollback
	void modifyDatabaseWithinTransaction() {
		// logic which uses the test data and modifies database state
	}

	@AfterEach
	void tearDownWithinTransaction() {
		// run "tear down" logic within the transaction
	}

	@AfterTransaction
	void verifyFinalDatabaseState() {
		// logic to verify the final state after transaction has rolled back
	}

}
@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {

	@BeforeTransaction
	fun verifyInitialDatabaseState() {
		// logic to verify the initial state before a transaction is started
	}

	@BeforeEach
	fun setUpTestDataWithinTransaction() {
		// set up test data within the transaction
	}

	@Test
	// overrides the class-level @Commit setting
	@Rollback
	fun modifyDatabaseWithinTransaction() {
		// logic which uses the test data and modifies database state
	}

	@AfterEach
	fun tearDownWithinTransaction() {
		// run "tear down" logic within the transaction
	}

	@AfterTransaction
	fun verifyFinalDatabaseState() {
		// logic to verify the final state after transaction has rolled back
	}

}
Avoid false positives when testing ORM code

当您测试操纵 Hibernate 会话或 JPA 持久性上下文状态的应用程序代码时,请务必在运行该代码的测试方法中刷新底层工作单元。未能刷新底层工作单元可能会产生错误肯定:您的测试通过,但相同的代码在实际生产环境中会出现异常。请注意,这适用于维护内存工作单元的任何 ORM 框架。在以下基于 Hibernate 的示例测试用例中,一种方法展示了错误肯定,而另一种方法正确地展示刷新会话的结果:

When you test application code that manipulates the state of a Hibernate session or JPA persistence context, make sure to flush the underlying unit of work within test methods that run that code. Failing to flush the underlying unit of work can produce false positives: Your test passes, but the same code throws an exception in a live, production environment. Note that this applies to any ORM framework that maintains an in-memory unit of work. In the following Hibernate-based example test case, one method demonstrates a false positive, and the other method correctly exposes the results of flushing the session:

Java
// ...

@Autowired
SessionFactory sessionFactory;

@Transactional
@Test // no expected exception!
public void falsePositive() {
	updateEntityInHibernateSession();
	// False positive: an exception will be thrown once the Hibernate
	// Session is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
public void updateWithSessionFlush() {
	updateEntityInHibernateSession();
	// Manual flush is required to avoid false positive in test
	sessionFactory.getCurrentSession().flush();
}

// ...
Kotlin
// ...

@Autowired
lateinit var sessionFactory: SessionFactory

@Transactional
@Test // no expected exception!
fun falsePositive() {
	updateEntityInHibernateSession()
	// False positive: an exception will be thrown once the Hibernate
	// Session is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
fun updateWithSessionFlush() {
	updateEntityInHibernateSession()
	// Manual flush is required to avoid false positive in test
	sessionFactory.getCurrentSession().flush()
}

// ...

以下示例显示了 JPA 的匹配方法:

The following example shows matching methods for JPA:

Java
// ...

@PersistenceContext
EntityManager entityManager;

@Transactional
@Test // no expected exception!
public void falsePositive() {
	updateEntityInJpaPersistenceContext();
	// False positive: an exception will be thrown once the JPA
	// EntityManager is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
public void updateWithEntityManagerFlush() {
	updateEntityInJpaPersistenceContext();
	// Manual flush is required to avoid false positive in test
	entityManager.flush();
}

// ...
Kotlin
// ...

@PersistenceContext
lateinit var entityManager:EntityManager

@Transactional
@Test // no expected exception!
fun falsePositive() {
	updateEntityInJpaPersistenceContext()
	// False positive: an exception will be thrown once the JPA
	// EntityManager is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
void updateWithEntityManagerFlush() {
	updateEntityInJpaPersistenceContext()
	// Manual flush is required to avoid false positive in test
	entityManager.flush()
}

// ...
Testing ORM entity lifecycle callbacks

类似于有关在测试 ORM 代码时避免 false positives 的注释,如果你的应用程序利用实体生命周期回调(也称为实体侦听器),请确保在运行该代码的测试方法内刷新底层的工作单元。未能_flush_ 或_clear_ 底层的工作单元可能会导致不调用某些生命周期回调。

Similar to the note about avoiding false positives when testing ORM code, if your application makes use of entity lifecycle callbacks (also known as entity listeners), make sure to flush the underlying unit of work within test methods that run that code. Failing to flush or clear the underlying unit of work can result in certain lifecycle callbacks not being invoked.

例如,当使用 JPA 时,@PostPersist@PreUpdate@PostUpdate 回调不会被调用,除非在保存或更新实体后调用了 entityManager.flush()。类似地,如果某个实体已经附加到当前工作单元(与当前持久性上下文相关联),则尝试重新加载该实体不会导致 @PostLoad 回调,除非在尝试重新加载该实体之前调用了 entityManager.clear()

For example, when using JPA, @PostPersist, @PreUpdate, and @PostUpdate callbacks will not be called unless entityManager.flush() is invoked after an entity has been saved or updated. Similarly, if an entity is already attached to the current unit of work (associated with the current persistence context), an attempt to reload the entity will not result in a @PostLoad callback unless entityManager.clear() is invoked before the attempt to reload the entity.

以下示例展示了如何刷新 EntityManager 以确保在持久化实体时调用 @PostPersist 回调。一个有 @PostPersist 回调方法的实体侦听器已为示例中使用的 Person 实体注册。

The following example shows how to flush the EntityManager to ensure that @PostPersist callbacks are invoked when an entity is persisted. An entity listener with a @PostPersist callback method has been registered for the Person entity used in the example.

Java
// ...

@Autowired
JpaPersonRepository repo;

@PersistenceContext
EntityManager entityManager;

@Transactional
@Test
void savePerson() {
	// EntityManager#persist(...) results in @PrePersist but not @PostPersist
	repo.save(new Person("Jane"));

	// Manual flush is required for @PostPersist callback to be invoked
	entityManager.flush();

	// Test code that relies on the @PostPersist callback
	// having been invoked...
}

// ...
Kotlin
// ...

@Autowired
lateinit var repo: JpaPersonRepository

@PersistenceContext
lateinit var entityManager: EntityManager

@Transactional
@Test
fun savePerson() {
	// EntityManager#persist(...) results in @PrePersist but not @PostPersist
	repo.save(Person("Jane"))

	// Manual flush is required for @PostPersist callback to be invoked
	entityManager.flush()

	// Test code that relies on the @PostPersist callback
	// having been invoked...
}

// ...

请参阅 Spring Framework 测试套件中的 JpaEntityListenerTests,以获取使用所有 JPA 生命周期回调的工作示例。

See JpaEntityListenerTests in the Spring Framework test suite for working examples using all JPA lifecycle callbacks.