批处理侦听器中的异常不会回滚作业事务

我将侦听器添加到块中以完成某些工作,如果失败,则我希望整个工作失败并在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吗?

brqtmars 回答:批处理侦听器中的异常不会回滚作业事务

暂时没有好的解决方案,如果你有好的解决方案,请发邮件至:iooj@foxmail.com
本文链接:https://www.f2er.com/3158315.html

大家都在问