Java 如何将Spring事务传播到另一个线程?
也许,我做错了什么,但我找不到一个好办法来解决下面的情况 我想对一个使用底层执行作业的服务进行单元测试。作业通过预先配置的Java 如何将Spring事务传播到另一个线程?,java,multithreading,spring,transactions,propagation,Java,Multithreading,Spring,Transactions,Propagation,也许,我做错了什么,但我找不到一个好办法来解决下面的情况 我想对一个使用底层执行作业的服务进行单元测试。作业通过预先配置的AsyncTaskExecutor在单独的线程中执行。在我的单元测试中,我想: 创建几个域对象并通过DAO持久化它们 调用服务方法以启动作业 等待作业完成 使用DAO检索域对象并检查其状态 显然,上述所有操作都应该在一个事务中执行,但不幸的是,(我理解这背后的原理) 我想到的想法是: 在步骤(1)之后提交事务#1。不好,因为单元测试后应该回滚DB状态 使用隔离。在作业配置中
AsyncTaskExecutor
在单独的线程中执行。在我的单元测试中,我想:
- 在步骤(1)之后提交事务#1。不好,因为单元测试后应该回滚DB状态
- 使用
。但测试和生产需要两种不同的配置隔离。在作业配置中读取\u UNCOMMITTED
但我同意,使用生产使用的相同策略是最好的。您的夹具数据有多大?如果有一个
setUp()
步骤将数据吹走并重新生成(如果数据很多,可能是从快照中生成的),情况会有多糟因此,您不必依赖回滚?我认为最简单的解决方案是在测试执行期间使用SyncTaskExecutor配置JobLauncher-这样,作业将在与测试相同的线程中执行,并共享事务
任务执行器配置可以移动到单独的spring配置xml文件中。它有两个版本—一个是在测试期间使用的SyncTaskExecutor,另一个是用于生产运行的AsyncTaskExecutor。虽然这不是您问题的真正解决方案,但我发现可以在工作线程内手动启动新事务。在某些情况下,这可能就足够了 来源: 例如:
@PersistenceContext
private EntityManager entityManager;
@Autowired
private PlatformTransactionManager txManager;
/* in a worker thread... */
public void run() {
TransactionStatus tx = txManager.getTransaction(new DefaultTransactionDefinition());
try {
entityManager.find(...)
...
entityManager.flush(...)
etc...
txManager.commit(tx);
} catch (RuntimeException e) {
txManager.rollback(tx);
}
}
我知道有些人不同意这一点,但是运行一个在运行后回滚的测试并不是一个很好的实践。特别是如果您使用的是ORM,因为在您执行select或flush会话之前,ORM不会发出任何insert/update命令。@Augusto:我觉得回滚功能很好,因为测试不会相互冲突。否则,每个单元测试类需要一个
setUp()
和一个test()
。人们可以使用补偿事务(例如,删除表单mytable
),但我不认为它符合Spring的理念。再说一次,我不认为每个人都同意我的建议。你需要确保你确实在测试一些东西,否则就像是没有断言的测试。谢谢你的评论。由于单元测试类中每个测试的测试数据(DB状态)应该不同,我想在每个测试开始时设置DB并提交它,但是我没有找到任何方法让TransactionStatus
传递到PlatformTransactionManager#commit()
。我认为,唯一的解决办法是让单元测试类由一个setUp()
和一个Test()
方法组成,然后在setUp()
中以某种编程方式启动Hibernate事务。我已经想象过那丑陋的代码…我想我还没有完全理解你的解决方案。请更详细一点。我不处理任何文件:处理步骤从数据库中读取一些内容,然后将一些内容写回数据库。初始化步骤需要使用一些值初始化DB。这不能作为单独的SQL脚本来完成(也许您将其称为“文件”),因为DB对象很复杂,应该使用DAO持久化。由于DAO方法是从主线程调用的,所以SUT看不到这些更改。您可以将任务执行器配置移动到单独的SpringXML文件中。此xml文件有两个版本—一个用于测试,将使用在同一线程中启动作业的SyncTaskExecutor—因此测试启动的事务也会传播到作业。另一个版本将包含AsyncTaskExecutor,它将用于实际的应用程序执行。是的,这是可行的,感谢您注意到这一点。当我想使用生产上下文/AsyncTaskExecutor进行端到端测试时,集成步骤如何?如果这样做,最好使用TransactionTemplate
@rustyx,但这不是将事务传播到另一个线程,而是创建新的线程。有没有办法让工作线程参与主线程创建的事务?