【发布时间】:2019-10-27 04:18:11
【问题描述】:
我有一个使用 JpaPagingItemReader 的小型 Spring Batch 作业。当我从命令行启动时,这项工作运行良好,但是当我想测试@StepScope 组件JpaPagingItemReader 时,我得到一个NullPointerException。我不明白为什么这项工作运作良好,但我无法根据文档对其进行测试。
我只有一个简单的Employee 类和基本的JPAannotations。
这是我的作业配置类:
@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 - 信息 - [主] { b.g.t.StepScopeIntegrationTest 47 } --> Context = {} / Step = retrieve-salary-slip-step 2019-06-12 17:17:32.970 - 信息 - [主] { b.g.t.StepScopeIntegrationTest 41 } --> 在执行之前,我们有 4 个条目 2019-06-12 17:17:33.002 - 信息 - [主] { 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(Native Method) 在 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。
【问题讨论】:
-
看起来
EntityManagerFactory没有在您的测试类中正确设置(因此它没有注入阅读器)。您确定 JPA 配置是由 Spring Boot 加载的吗?您可以尝试添加@SpringBootTest吗?两个旁注:1. 我不明白为什么你的阅读器应该是步进范围的,2. 在你的测试方法中调用read之前应该打开阅读器以兑现ItemStream合同。 -
@MahmoudBenHassine 非常感谢! 1)你是对的,读者不必被限定。 2)我首先通过调用
ItemReader.open()进行了测试,现在它可以工作了。也许您可以在文档中添加这一点?目前在文档中,我们有一个不需要调用 open() 方法的“ItemReader”测试的简单示例...... -
太棒了!很高兴它有帮助。关于docs,有一个代码注释说“读者已初始化”,我认为这指的是
open方法。然而,并不是所有的读者都实现了ItemStream,人们可以直接为这些读者调用read。在您的情况下,JpaPagingItemReader是一个项目流,这就是为什么它需要在用于读取数据之前打开/初始化。 HTH。
标签: java jpa spring-batch h2