Spring Batch JpaPagingItemReader在测试(NPE)中没有EntityManager

时间:2019-06-12 15:32:23

标签: java jpa spring-batch h2

我有一个使用JpaPagingItemReader的小型Spring Batch作业。当我从命令行启动时,该工作效果很好,但是当我想测试@StepScope组件JpaPagingItemReader时,我得到了NullPointerException。我不明白为什么这项工作会顺利进行,但是我无法按照文档进行测试。

我只有一个简单的Employee类,带有基本的JPA注释。

这是我的工作配置类:

@Slf4j
@Configuration
@EnableBatchProcessing
public class PaySalaryJobConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Autowired
    public PaySalaryJobConfiguration(
            JobBuilderFactory jobBuilderFactory,
            StepBuilderFactory stepBuilderFactory
    ) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    @StepScope
    public ItemProcessor<Employee, Employee> employeeProcessor() {
        return item -> {
            log.info("Process item: {}", item);
            return item;
        };
    }

    @Bean
    @StepScope
    public JpaPagingItemReader<Employee> someoneReader(
            EntityManagerFactory entityManagerFactory
    ) {
        return new JpaPagingItemReaderBuilder<Employee>()
                .name("someone-reader")
                .entityManagerFactory(entityManagerFactory)
                .queryString("Select e from Employee e order by e.id asc")
                .pageSize(1)
                .build();
    }

    @Bean
    @StepScope
    public FlatFileItemWriter<Employee> csvWriter(
            @Value("#{jobParameters['output.path.csv']}") String outputPath
    ) {
        DelimitedLineAggregator<Employee> lineAggregator = new DelimitedLineAggregator<>();
        lineAggregator.setDelimiter(",");

        BeanWrapperFieldExtractor<Employee> fieldExtractor = new BeanWrapperFieldExtractor<>();
        fieldExtractor.setNames(new String[]{"id", "firstName", "lastName", "salary"});
        lineAggregator.setFieldExtractor(fieldExtractor);

        return new FlatFileItemWriterBuilder<Employee>()
                .name("csv-salary-writer")
                .resource(new FileSystemResource(outputPath))
                .lineAggregator(lineAggregator)
                .encoding("UTF-8")
                .build();
    }

    @Bean
    public Step writeSalarySlipToCsv (
            JpaPagingItemReader<Employee> someoneReader,
            FlatFileItemWriter<Employee> csvWriter
    ) {
        return stepBuilderFactory
                .get("retrieve-salary-slip-step")
                .<Employee, Employee>chunk(1)
                .reader(someoneReader)
                .processor(employeeProcessor())
                .writer(csvWriter)
                .stream(someoneReader)
                .build();
    }

    @Bean
    public Job paySalaryJob(
            Step writeSalarySlipToCsv
    ) {
        return jobBuilderFactory
                .get("pay-salary-job")
                .incrementer(new RunIdIncrementer())
                .start(writeSalarySlipToCsv)
                .build();
    }
}

这是我的测试课:

@Slf4j
@SpringBatchTest
@RunWith(SpringRunner.class)
@EnableAutoConfiguration
@ContextConfiguration(classes = PaySalaryJobConfiguration.class)
public class StepScopeIntegrationTest {

    @Autowired
    private JpaPagingItemReader<Employee> someoneReader;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Before
    public void setUp() {
        log.info("Before execution we have {} entries", jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class ));
    }

    public StepExecution getStepExecution() {
        log.info("Step Execution !");
        StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution("retrieve-salary-slip-step", 1564L);
        log.info("Context = {} / Step = {} ", stepExecution.getExecutionContext(), stepExecution.getStepName());
        return stepExecution;
    }

    @Test
    public void testReader() throws Exception {
        log.info("Page = {}", someoneReader.getPage());
        log.info("PageSize = {}", someoneReader.getPageSize());
        Assert.assertNotNull(someoneReader.read());
    }
}

问题在第192行的JpaPagingItemReader类中发生:

@Override
@SuppressWarnings("unchecked")
protected void doReadPage() {

    EntityTransaction tx = null;

    if (transacted) {
        tx = entityManager.getTransaction(); // EntityManager is null...
        tx.begin();

        entityManager.flush();
        entityManager.clear();
    }//end if

这是测试执行的堆栈跟踪:

  

2019-06-12 17:17:32.845-信息-[主要] {   b.g.t.StepScopeIntegrationTest 45}->步骤执行! 2019-06-12   17:17:32.845-信息-[main] {b.g.t. StepScopeIntegrationTest 47   }->上下文= {} /步骤=检索薪水滑移步骤2019-06-12   17:17:32.970-信息-[主要] {b.g.t. StepScopeIntegrationTest 41   }->执行之前,我们有4个条目2019-06-12 17:17:33.002-   信息-[main] {b.g.t.StepScopeIntegrationTest 53}->页面= 0   2019-06-12 17:17:33.002-信息-[主要] {   b.g.t.StepScopeIntegrationTest 54}-> PageSize = 1

     

java.lang.NullPointerException在   org.springframework.batch.item.database.JpaPagingItemReader.doReadPage(JpaPagingItemReader.java:192)     在   org.springframework.batch.item.database.AbstractPagingItemReader.doRead(AbstractPagingItemReader.java:108)     在   org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:89)     在   org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader $$ FastClassBySpringCGLIB $$ ebb633d0.invoke()     在   org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)     在   org.springframework.aop.framework.CglibAopProxy $ CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)     在   org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)     在   org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)     在   org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)     在   org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)     在   org.springframework.aop.framework.CglibAopProxy $ DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)     在   org.springframework.batch.item.database.JpaPagingItemReader $$ EnhancerBySpringCGLIB $$ b998315d.read()     在   be.groups.test.StepScopeIntegrationTest.testReader(StepScopeIntegrationTest.java:55)     在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处   sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     在   sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     在java.lang.reflect.Method.invoke(Method.java:498)在   org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall(FrameworkMethod.java:50)     在   org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)     在   org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)     在   org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)     在   org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)     在   org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)     在   org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)     在   org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)     在   org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)     在   org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)     在org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)处   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)     在   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)     在org.junit.runners.ParentRunner处$ 3.run(ParentRunner.java:290)在   org.junit.runners.ParentRunner $ 1.schedule(ParentRunner.java:71)在   org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)在   org.junit.runners.ParentRunner.access $ 000(ParentRunner.java:58)在   org.junit.runners.ParentRunner $ 2.evaluate(ParentRunner.java:268)在   org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)     在   org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)     在org.junit.runners.ParentRunner.run(ParentRunner.java:363)处   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)     在org.junit.runner.JUnitCore.run(JUnitCore.java:137)处   com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)     在   com.intellij.rt.execution.junit.IdeaTestRunner $ Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)     在   com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)     在   com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

我正在使用h2数据库,并且在数据库中填充了import.sql和schema-h2.sql脚本提供的启动数据。我正在使用Spring-Boot 2.0.5.RELEASE和Spring Batch Core 4.1.2.RELEASE。

2 个答案:

答案 0 :(得分:0)

正如Mahmoud Ben Hassine在评论中建议的那样,我将测试班级更改为:

@Slf4j
//@SpringBootTest
@SpringBatchTest
@RunWith(SpringRunner.class)
@EnableAutoConfiguration
@ContextConfiguration(classes = {
        PaySalaryJobConfiguration.class
})
@ActiveProfiles("test")
public class StepScopeIntegrationTest {

    @Autowired
    private JpaPagingItemReader<Employee> someoneReader;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private StepExecution stepExecution;

    @Before
    public void setUp() {
        log.info("Before execution we have {} entries", jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class ));
    }

    public StepExecution getStepExecution() {
        log.info("Step Execution !");
        stepExecution = MetaDataInstanceFactory.createStepExecution();
        log.info("Context = {} / Step = {} ", stepExecution.getExecutionContext(), stepExecution.getStepName());
        return stepExecution;
    }

    @Test
    public void testReader() throws Exception {
        log.info("Page = {}", someoneReader.getPage());
        log.info("PageSize = {}", someoneReader.getPageSize());
        someoneReader.open(stepExecution.getExecutionContext());
        Assert.assertNotNull(someoneReader.read());
    }
}

现在测试运行良好。

答案 1 :(得分:0)

这是不正确的。在测试范围之外,您可能会遇到相同的问题。正如此JIRA错误中所述,您需要定义Bean以返回ItemStreamReader对象而不是JpaPagingItemReader。

这是吉米·普拉特(Jimmy Praet)的原始评论

未调用您作家的open()方法,因为作家 未注册为流。您的bean定义方法应 返回一个ItemStreamReader而不是ItemReader,以便它被注册 作为自动流。

这里是参考 https://jira.spring.io/si/jira.issueviews:issue-html/BATCH-2256/BATCH-2256.html