我有一个Spring Batch应用程序,它将通过rest API读取数据并将数据写入DB。
下面是我的代码。
配置:
@Bean
@Qualifier("batchJob")
public Job batchJob() {
return jobBuilderFactory.get("batchJob").incrementer(new RunIdIncrementer()).listener(batchJobExecutionListener)
.start(firstStep)
.next(secondStep)
.build();
}
@Bean
public Step firstStep() {
return stepBuilderFactory.get("firstStep").<Input, Output>chunk(AppConst.BATCH_CHUNK)
.reader(firstReader)
.processor(firstProcessor)
.writer(firstWriter)
.faultTolerant()
.skip(Exception.class)
.skipLimit(99999)
.listener(new FirstSkipListener())
.build();
}
FirstSkipListener.class:
@Component
@Slf4j
public class FirstSkipListener implements SkipListener<Input, Output> {
@Override
public void onSkipInProcess(Input arg0, Throwable arg1) {
// Do nothing in onSkipInProcess
}
@Override
public void onSkipInRead(Throwable arg0) {
// Do nothing in onSkipInRead
}
@Override
public void onSkipInWrite(Output output, Throwable arg1) {
log.error("Skipped output = {} ", output);
}
}
FirstWriter.class:
@Component
@Slf4j
public class FirstWriter implements ItemWriter<Output> {
@Autowired
private OutputRepository outputRepository;
@Autowired
private BatchRunRepository batchRunRepository;
private Integer batchId;
private Integer updateCount;
/**
* This method will write output list in db.
* @param outputs
* @return void
* @throws Exception
*/
@Override
public void write(List<? extends Output> outputs) {
log.info("Start writing output data.");
if(!CollectionUtils.isEmpty(outputs)) {
try {
updateCount = outputs.size();
outputRepository.saveAll(outputs);
outputRepository.flush();
log.info("End writing output data.");
} catch (Exception e) {
log.error("Error occured while writing output data: {}", e.getMessage());
}
} else {
log.info("No data to be writtern for output.");
}
}
}
OutputRepository.class:
@Repository
public interface OutputRepository extends JpaRepository<Output, Long> {
}
application.yml:
spring:
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
default_schema: my_schema
format_sql: true
jdbc:
lob:
non_contextual_creation: true
batch:
initialize-schema: always
initializer:
enabled: false
job:
enabled: false
main:
allow-bean-definition-overriding: true
jackson:
serialization:
indent_output: true
profiles: local
datasource:
url: jdbc:postgresql://localhost:5432/mydb?currentSchema=my_schema
username: postgres
password: 12345
platform: postgres
jpa:
show-sql: false
hibernate:
ddl-auto: validate
现在,当我开始工作时,我正处于例外之下。
2020-10-28 17:56:01.520 INFO [my-app,5764b6867c680cf1,5764b6867c680cf1,false] 17548 --- [nio-8080-exec-1] c.m.c.d.m.b.job.writer.FirstWriter : Start writing output data.
2020-10-28 17:56:02.108 ERROR [my-app,5764b6867c680cf1,5764b6867c680cf1,false] 17548 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: null value in column "output_name" violates not-null constraint
Detail: Failing row contains (486, null, 4, null, null, null, null, Need Description, data, null, 439e00ef-45d5-3a4b-ab65-f620f641b6d3, f, A, user, 2020-10-28 17:56:02.101, 2020-10-28 17:56:02.101, user, null, ).
2020-10-28 17:56:02.118 ERROR [my-app,5764b6867c680cf1,5764b6867c680cf1,false] 17548 --- [nio-8080-exec-1] c.m.c.d.m.b.job.writer.FirstWriter : Error occured while writing output data: could not execute statement; SQL [n/a]; constraint [output_name]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
2020-10-28 17:56:02.250 ERROR [my-app,5764b6867c680cf1,5764b6867c680cf1,false] 17548 --- [nio-8080-exec-1] o.s.batch.core.step.tasklet.TaskletStep : JobRepository failure forcing rollback
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [UPDATE BATCH_STEP_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE STEP_EXECUTION_ID = ?]; SQL state [25P02]; error code [0]; ERROR: current transaction is aborted, commands ignored until end of transaction block; nested exception is org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:89) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:862) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:917) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao.persistSerializedContext(JdbcExecutionContextDao.java:236) ~[spring-batch-core-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao.updateExecutionContext(JdbcExecutionContextDao.java:163) ~[spring-batch-core-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.batch.core.repository.support.SimpleJobRepository.updateExecutionContext(SimpleJobRepository.java:210) ~[spring-batch-core-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_261]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_261]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_261]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_261]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) [spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) [spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) [spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at com.sun.proxy.$Proxy158.updateExecutionContext(Unknown Source) ~[na:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_261]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_261]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_261]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_261]
在我的代码中,我正在设置跳过策略,并且期望如果我最初在输出列表中传递10个元素并且第5条记录出现问题,那么spring batch应该抛出异常并重试调用write方法10次。这样一来,只有一条记录将被跳过,而另外9条将保留在数据库中。
但就我而言,我的批处理应用程序完全停止了。甚至无法更新sprint维护的BATCH_STEP_EXECUTION_CONTEXT表。
请注意,我正在使用JPA存储库,而不是尝试在任何地方管理事务。甚至没有考虑考虑spring的事务管理器会代表我管理它。
答案 0 :(得分:0)
好吧,我得到了错误。在我的写课中,我有一个catch块,用于打印和异常。我删除了try catch块并让spring批处理查找异常并调用跳过监听器。