首页 > 解决方案 > StoredProcedureItemReader 无法重试死锁异常

问题描述

一旦由于数据库上的死锁异常而失败,我很难让我的 StoredProcedureItemReader 重试。
这是我的步骤过程的配置:

@Bean    
public Step Step() throws Exception {       
            return stepBuilderFactory.get("Step")
                    .<Student, Student>chunk(100)
                    .reader(storedProcItemReader())
                    .processor(studentItemProcessor)
                    .writer(fileItemWriter())
                    .faultTolerant()
                    .retryLimit(5)
                    .retry(myException.class)
                    .backOffPolicy(backoffPolicy())
                    .build();
    }

    @Bean
    @StepScope
    public StoredProcedureItemReader<Student> storedProcItemReader() throws Exception {
        return studentItemReader.getDataFromDatabase(dataSource);
    }

studentItemReader 类文件:

@Component
@StepScope
public class studentItemReader{

    @Retryable(include = {DataAccessException.class, JDBCException.class, TransactionException.class, DeadlockLoserDataAccessException.class, Exception.class }, maxAttempts = 5, backoff = @Backoff(delay = 1000,
            maxDelay = 15000, multiplier = 2))
    public StoredProcedureItemReader<Student> getDataFromDatabase(DataSource dataSource) {
        
            StoredProcedureItemReader<RegionResponse> reader = new StoredProcedureItemReader<>();
            
            SqlParameter[] parameter = { new SqlParameter("@studentId",
                    java.sql.Types.INTEGER) };
            PreparedStatementSetter statementValues = new PreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps) throws SQLException {
                    ps.setInt(1, parameterValue);
                    }
            };
            reader.setDataSource(dataSource);
            reader.setProcedureName("dbo.StudentReport");
            reader.setParameters(parameter);
            reader.setPreparedStatementSetter(statementValues);
            reader.setRowMapper(new StudentRowMapper());
            return reader;
        }   
    }
}

所以,问题是在我的 StoredProcedureItemReader 上添加 Retry 后,我无法让 Retry 部分工作。请让我在这里做错了什么。提前致谢!

标签: springspring-batchbatch-processing

解决方案


您正在添加@Retryable一个返回项目阅读器的方法。此方法仅在配置时调用。如果您想在抛出异常时在运行时重试读取操作,您应该在read阅读器的方法上添加注释。添加重试功能的基于注释的方法可能很棘手,这就是为什么我建议使用编程方式来查看实际重试发生的位置。这是一个简单的例子:

import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.database.StoredProcedureItemReader;
import org.springframework.retry.support.RetryTemplate;

public class RetryableItemReader<T> implements ItemStreamReader<T> {

    private final StoredProcedureItemReader<T> delegate;
    private final RetryTemplate retryTemplate;

    public RetryableItemReader(StoredProcedureItemReader<T> delegate, RetryTemplate retryTemplate) {
        this.delegate = delegate;
        this.retryTemplate = retryTemplate;
    }

    @Override
    public T read() throws Exception {
        return retryTemplate.execute(context -> delegate.read());
    }

    @Override
    public void open(ExecutionContext executionContext) throws ItemStreamException {
        delegate.open(executionContext);
    }

    @Override
    public void update(ExecutionContext executionContext) throws ItemStreamException {
        delegate.update(executionContext);
    }

    @Override
    public void close() throws ItemStreamException {
        delegate.close();
    }
}

然后,您可以在步骤中将其用作项目阅读器:

@Bean
@StepScope
public RetryableItemReader<Student> storedProcItemReader() {
    RetryTemplate retryTemplate = new RetryTemplateBuilder()
            // define your retry policy
            .build();
    StoredProcedureItemReader<Student> delegateItemReader = new StoredProcedureItemReaderBuilder<Student>()
            // define your delegate item reader
            .build();
    return new RetryableItemReader<>(delegateItemReader, retryTemplate);
}

推荐阅读