Spring Batch - 如何使用 jobLauncherTestUtils 防止数据库提交

Posted

技术标签:

【中文标题】Spring Batch - 如何使用 jobLauncherTestUtils 防止数据库提交【英文标题】:Spring Batch - How to prevent database commits with jobLauncherTestUtils 【发布时间】:2017-01-29 22:22:35 【问题描述】:

我有写入数据库的 Spring Batch 作业(它有一个带有 JpaItemWriter 的步骤)。我有一个集成测试,如下所示:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("integrationTest")
public class LoadApplicationTests 

    @Autowired
    private Job job;

    @Autowired
    private JobRepository jobRepository;

    @Autowired
    private JobLauncher jobLauncher;

    private JobLauncherTestUtils jobLauncherTestUtils;

    @Before
    public void setUp() throws IOException, java.text.ParseException, Exception        
        jobLauncherTestUtils = new JobLauncherTestUtils();
        jobLauncherTestUtils.setJob(job);
        jobRepository = new MapJobRepositoryFactoryBean(new ResourcelessTransactionManager()).getObject();
        jobLauncherTestUtils.setJobRepository(jobRepository);
        jobLauncherTestUtils.setJobLauncher(jobLauncher);
    

    @Test
    public void testJob() throws Exception 
        JobParametersBuilder j = new JobParametersBuilder();
        JobParameters jobParameters = j.addDate("runDate", new Date())
                .addString("file", testFile.getAbsolutePath())
                .addString("override", "false")
                .addString("weekly", "false")
                .toJobParameters();

        JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);

        Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
    

在测试中运行作业时,它会提交到数据库。如何防止提交到数据库?通常,我可以添加@Transactional 以在每次测试后回滚事务。但是,当我在测试类中添加注释时,我收到:

java.lang.IllegalStateException: Existing transaction detected in JobRepository. Please fix this and try again (e.g. remove @Transactional annotations from client).

更新

我尝试将@Rollback 添加到测试类。但是,JpaItemWriter 仍然提交。

这是应用程序代码中事务管理器的配置:

@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) 
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory);
    return transactionManager;


@Bean
public Step stepLoadFile(StepBuilderFactory stepBuilderFactory, 
        PlatformTransactionManager transactionManager,
        ItemReader<MyClass> reader, ItemProcessor<MyClass, 
        MyClass> processor, 
        ItemWriter<MyClass> writer, 
        ReadFailureHandler readListenerSupport,
        WriteFailureHandler writeListenerSupport) 
    Step step = stepBuilderFactory.get("stepPersistFile")
            .transactionManager(transactionManager)
            .<MyClass, MyClass> chunk(1000)      
            .reader(reader)
            .processor(processor)
            .listener(writeListenerSupport)
            .listener(readListenerSupport)
            .writer(writer)
            .build();

    return step;

【问题讨论】:

我假设您已经检查过您的数据源/连接未处于自动提交模式? 感谢您的评论。对于相同的数据源/连接,我有使用@Transactional 注释的存储库测试。这些测试保存到数据库并在每次测试结束后回滚。因此,它似乎不在自动提交模式下。我想知道 Spring Batch 是否在对launchJob 的调用中提交事务。因此,测试没有任何回滚,因为它已经在对 launchJob 的调用中提交。 【参考方案1】:

我猜你有transactionManager,如果有,添加

@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)

在你的测试类的顶部。

【讨论】:

感谢您的回答。我将JpaTransactionManager 用于批处理作业。我正在使用 Spring boot 1.4.0.RELEASE,它为我提供了 Spring Framework 4.3.2.RELEASE。在该版本中,@TransactionConfiguration 已弃用。无论如何我尝试了你的建议。错误被阻止,但JpaItemWriter 提交到数据库。 嗯,你试过@Rollback 吗?如果@transactionConfiguration 不起作用,我不打算这样做,但仍然......docs.spring.io/spring-framework/docs/current/javadoc-api/org/… 谢谢。我确实尝试过@Rollback,但它没有用。请参阅 OP 中的更新部分。【参考方案2】:

为了克服这个问题,我的小组简单地编写了一个@After 挂钩来清除写入的数据。它不漂亮,也不受欢迎,但它似乎让我们解决了我们的问题。

请记住,这仍会将您的作业执行写入batch_job_executionbatch_job_execution_context 等。

还要认识到,您可能希望确保在您的 test/resources/application.[properties|yml] 中将您的 spring.batch.job.enabled 设置为 false

欢乐时光?‍♂️

【讨论】:

您也可以利用JobRepositoryTestUtils 删除@After 挂钩中的作业执行数据。【参考方案3】:

@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) 可能是您正在寻找的,也是我过去在测试更改数据库记录的程序时使用的。基于Spring Docs,@DirtiesContext是一个

Test 注解,表明与测试关联的 ApplicationContext 是脏的,因此应该关闭并从上下文缓存中删除。 如果测试修改了上下文,请使用此注解 - 例如,通过修改单例 bean 的状态、修改嵌入式数据库的状态等。请求相同上下文的后续测试将提供新的上下文。

否则,基于@Dan 的答案,TestTransaction 可能允许在用@Test@Before@After 注释的方法中更明确地控制您的测试事务。请参阅here 了解更多信息。

【讨论】:

【参考方案4】:

通常我用于集成测试的策略是为集成测试创建profile(如您所见)和Embedded DB,并使用特定的application-test.properties,因此当您在担心数据修改和这些东西。

关于您的情况,看起来您添加@Transactional 以进行测试是正确的,正如我们在this topic 上看到的那样。然后我会花时间进一步调查发生此错误时遇到的异常。

java.lang.IllegalStateException: Existing transaction detected in JobRepository. Please fix this and try again (e.g. remove @Transactional annotations from client).

此外,这个异常看起来是众所周知的,所以我建议查看this other topic 以了解尝试过的内容。他们有几乎相同的问题,并写了一篇关于如何避免发生此错误的文章(也在此线程上)。

评论的另一种可能性是upgrade spring code version,因为在某些情况下,此问题在升级后立即开始发生。

现在,更多地谈论使用的组件,您使用spring-batch,它具有测试批处理切片等的特定套件,因此我建议您查看how to use/test spring-batch 和spring-batch test documentation本身。也许使用spring-boot 提供的组件进行批量测试,有预先制定的决策和策略可以缓解您的问题。

【讨论】:

以上是关于Spring Batch - 如何使用 jobLauncherTestUtils 防止数据库提交的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Batch 中使用 Spring 事务支持

如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?

如何将 Spring Batch Cron 作业迁移到 Spring Cloud 任务

如何在 Spring Batch 中使用 Resource ItemReader

Spring Batch - 如何使用 jobLauncherTestUtils 防止数据库提交

如何使用 CommandLineJobRunner 调用嵌入在 Spring Boot 应用程序中的 Spring Batch 作业