我将侦听器添加到块中以完成某些工作,如果失败,则我希望整个工作失败并在ItemWriter中的数据库中回滚插入。 但这并没有回滚。 我猜@BeforeChunk和@AfterChunk不在块内,因此它不在同一事务内,或者@AfterChunkError只是捕获错误,我应该传播该错误以告诉作业这是一个错误并回滚所有内容。
它部署在Weblogic 12.2.1.3.0下,我使用Spring Boot 2.3.0。 在Weblogic for datasource中,我使用oracle.jdbc.replay.OracleXADataSourceImpl驱动程序类名称。 我的pom文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
...
</project>
application.properties是:
spring.batch.job.enabled=false
spring.datasource.jndi-name=jdbc/NameDS
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.batch.initialize-schema=never
我的配置是:
@Autowired
DataSource dataSource;
@Autowired
Processor processor;
@Autowired
Writer writer;
...
private Properties getJNDiProperties() {
final Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
return jndiProps;
}
@Bean
public JndiTemplate jndiTemplate() {
final JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.setEnvironment(getJNDiProperties());
return jndiTemplate;
}
@Bean
public JdbcCursorItemReader<Object> itemsReader() {
JdbcCursorItemReader<Object> customerJdbcCursorItemReader = new JdbcCursorItemReader<>();
customerJdbcCursorItemReader.setDataSource(dataSource);
customerJdbcCursorItemReader.setSql(env.getProperty("select * from table"));
customerJdbcCursorItemReader.setRowMapper(new BeanPropertyRowMapper<>(Object.class));
return customerJdbcCursorItemReader;
}
@Bean
public Job registrationChunkJob() {
return jobBuilderFactory.get("jobRegister")
.incrementer(new RunIdIncrementer())
.flow(step()).end().build();
}
@Bean
TaskExecutor taskExecutorStepPush() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(2);
taskExecutor.setMaxPoolSize(20);
taskExecutor.setQueueCapacity(4);
taskExecutor.setAllowCoreThreadTimeOut(true);
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setThreadNamePrefix(LoggingUtil.getWeblogicName() + "-");
return taskExecutor;
}
@Bean
public Step step() {
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
attribute.setPropagationBehavior(Propagation.REQUIRED.value());
attribute.setIsolationLevel(Isolation.READ_COMMITTED.value());
return stepBuilderFactory.get("stepRegister").<Object, Object>chunk(50)
.reader(itemsReader())
.processor(processor)
.writer(writer)
.listener(chunkTransaction)
.taskExecutor(taskExecutorStepPush())
.throttleLimit(1)
.transactionAttribute(attribute)
.build();
}
ChunkTransaction类如下:
@Slf4j
@Component
public class ChunkTransaction {
....
@BeforeChunk
public void onChunkStart(ChunkContext context) throws IOException {
log.debug("onChunkStart");
// do something
}
@AfterChunk
public void onChunkEnd(ChunkContext context) throws IOException {
log.debug("onChunkEnd");
// HERE IS AN ERROR!!!!!!!!!
}
@AfterChunkError
public void onChunkError(ChunkContext context) throws IOException {
log.debug("onChunkError");
// do something
}
}
处理器类如下:
@Slf4j
@Component
public class Processor implements ItemProcessor<Object Object> {
@Override
public Object process(Object object) throws Exception {
log.debug("Processing");
return object;
}
}
Writer类如下:
@Slf4j
@Component
public class Writer implements ItemWriter<Object Object> {
@Override
public void write(List<? extends Object> list) {
log.debug("-------------------- WRITE TO DATABASE --------------------");
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(list.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate("update table set COLUMN1= :column1, COLUMN2 = :column2 where ID = :id", batch);
log.info(" Updated " + updateCounts.length + " successfully");
}
}
每5分钟开始工作,申请开始后2分钟
@Component
@Slf4j
public class JobMain {
@Autowired
JobLauncher jobLauncher;
@Retryable
@Scheduled(initialDelay = 120000, fixedRate = 300000)
public void launchJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
JobParameters jobParameters = new JobParametersBuilder()
.addString("jobRegister", String.valueOf(System.currentTimeMillis()))
.toJobParameters();
jobLauncher.run(job, jobParameters);
}
}
这是日志:
[ AdminServer-1] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepRegister]
[ AdminServer-1] c.e.c.l.CocoaChunkTransactionIntegration : onChunkStart
[ AdminServer-1] e.e.e.c.c.e.c.chunk.Processor : Processing
...
[ AdminServer-1] e.e.e.c.c.e.core.chunk.DocumentWriter : -------------------- WRITE TO DATABASE --------------------
[ AdminServer-1] e.e.e.c.c.e.core.chunk.DocumentWriter : Updated 50 successfully
[ AdminServer-1] c.e.c.l.CocoaChunkTransactionIntegration : onChunkEnd <-- AFTER THIS IS AN ERROR!!!!!
[ AdminServer-2] c.e.c.l.CocoaChunkTransactionIntegration : onChunkStart <-- HERE IS NEW THREAD HALF SECOND AFTER "onChunkEnd" log !!!!
[ AdminServer-2] c.e.c.l.CocoaChunkTransactionIntegration : onChunkError <-- THIS ERROR IS IN THAT NEW THREAD
好吧,正如我所想 它说(https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/core/ChunkListener.html):
afterChunk
Callback after the chunk is executed, outside the transaction.
因此,我可以在ItemWriter方法内的@AfterChunk方法中执行某些操作,如果失败,它将引发异常,这将导致作业回滚,我必须捕获此错误才能手动回滚其他事务。
我希望有一些更漂亮的方法可以将其他交易添加到该交易中。 向块侦听器类添加@Transactional注释不起作用。
那么,我可以在Writer类中获取#chunkContext吗?