Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何将Spring事务传播到另一个线程?_Java_Multithreading_Spring_Transactions_Propagation - Fatal编程技术网

Java 如何将Spring事务传播到另一个线程?

Java 如何将Spring事务传播到另一个线程?,java,multithreading,spring,transactions,propagation,Java,Multithreading,Spring,Transactions,Propagation,也许,我做错了什么,但我找不到一个好办法来解决下面的情况 我想对一个使用底层执行作业的服务进行单元测试。作业通过预先配置的AsyncTaskExecutor在单独的线程中执行。在我的单元测试中,我想: 创建几个域对象并通过DAO持久化它们 调用服务方法以启动作业 等待作业完成 使用DAO检索域对象并检查其状态 显然,上述所有操作都应该在一个事务中执行,但不幸的是,(我理解这背后的原理) 我想到的想法是: 在步骤(1)之后提交事务#1。不好,因为单元测试后应该回滚DB状态 使用隔离。在作业配置中

也许,我做错了什么,但我找不到一个好办法来解决下面的情况

我想对一个使用底层执行作业的服务进行单元测试。作业通过预先配置的
AsyncTaskExecutor
在单独的线程中执行。在我的单元测试中,我想:

  • 创建几个域对象并通过DAO持久化它们
  • 调用服务方法以启动作业
  • 等待作业完成
  • 使用DAO检索域对象并检查其状态
  • 显然,上述所有操作都应该在一个事务中执行,但不幸的是,(我理解这背后的原理)

    我想到的想法是:

    • 在步骤(1)之后提交事务#1。不好,因为单元测试后应该回滚DB状态
    • 使用
      隔离。在作业配置中读取\u UNCOMMITTED
      。但测试和生产需要两种不同的配置

    如果您确实想要单独的配置,我建议您在配置中模板化隔离策略,并从属性文件中获取其值,这样您就不会得到一组不同的Spring配置来进行测试和生产


    但我同意,使用生产使用的相同策略是最好的。您的夹具数据有多大?如果有一个
    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,但这不是将事务传播到另一个线程,而是创建新的线程。有没有办法让工作线程参与主线程创建的事务?