最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Batch實(shí)戰(zhàn)示例

 更新時間:2026年02月07日 09:16:14   作者:雨中飄蕩的記憶  
批量數(shù)據(jù)處理是一個非常常見的需求,比如月底的工資代發(fā)、銀行對賬、數(shù)據(jù)報表生成等,當(dāng)數(shù)據(jù)量達(dá)到幾十萬甚至上百萬時,可以高效的完成,感興趣的可以了解一下

前言

在企業(yè)級應(yīng)用中,批量數(shù)據(jù)處理是一個非常常見的需求。比如月底的工資代發(fā)、銀行對賬、數(shù)據(jù)報表生成等。當(dāng)數(shù)據(jù)量達(dá)到幾十萬甚至上百萬時,如何高效、可靠地處理這些數(shù)據(jù),就成了一個技術(shù)挑戰(zhàn)。

本文將以"50萬筆工資代發(fā)"為實(shí)際場景,詳細(xì)介紹如何使用Spring Batch框架來處理大規(guī)模批量數(shù)據(jù),并重點(diǎn)講解當(dāng)處理失敗時,如何實(shí)現(xiàn)部分回滾機(jī)制,確保已成功處理的數(shù)據(jù)不會因?yàn)樯倭渴∮涗浂炕貪L。

一、什么是Spring Batch?

1.1 Spring Batch簡介

Spring Batch是一個輕量級的、全面的批處理框架,由Spring團(tuán)隊(duì)開發(fā),旨在幫助企業(yè)開發(fā)健壯的批處理應(yīng)用程序。它于2008年首次發(fā)布,經(jīng)過十多年的發(fā)展,已經(jīng)成為Java批處理領(lǐng)域的事實(shí)標(biāo)準(zhǔn)。

Spring Batch的核心設(shè)計理念包括:

  • Chunk-oriented Processing(塊級處理):將大量數(shù)據(jù)分批處理,避免內(nèi)存溢出
  • 事務(wù)管理:每個Chunk作為一個獨(dú)立的事務(wù),支持部分回滾
  • 容錯機(jī)制:支持跳過(Skip)、重試(Retry)等容錯策略
  • 作業(yè)調(diào)度:支持定時任務(wù)、手動觸發(fā)等多種調(diào)度方式
  • 監(jiān)控與統(tǒng)計:提供完整的執(zhí)行記錄和統(tǒng)計信息

1.2 核心概念詳解

Job(作業(yè))

Job是批處理的核心概念,代表一個完整的批處理任務(wù)。一個Job可以包含多個Step,按順序或并行執(zhí)行。

@Bean
public Job salaryPaymentJob() {
    return jobBuilderFactory.get("salaryPaymentJob")
        .start(step1())
        .next(step2())
        .build();
}

Step(步驟)

Step是Job的基本執(zhí)行單元,每個Step包含:

  • ItemReader:讀取數(shù)據(jù)
  • ItemProcessor:處理數(shù)據(jù)(可選)
  • ItemWriter:寫入數(shù)據(jù)
@Bean
public Step salaryPaymentStep() {
    return stepBuilderFactory.get("salaryPaymentStep")
        .<SalaryPayment, SalaryPayment>chunk(1000)
        .reader(reader())
        .processor(processor())
        .writer(writer())
        .build();
}

Chunk(數(shù)據(jù)塊)

Chunk是Spring Batch處理數(shù)據(jù)的基本單位。每次從Reader讀取指定數(shù)量的記錄,處理后一起提交到數(shù)據(jù)庫:

讀取1000條 → 處理1000條 → 寫入1000條 → 提交事務(wù)

1.3 應(yīng)用場景

Spring Batch適用于以下典型場景:

場景描述示例
數(shù)據(jù)遷移跨系統(tǒng)數(shù)據(jù)同步從舊系統(tǒng)遷移數(shù)據(jù)到新系統(tǒng)
數(shù)據(jù)轉(zhuǎn)換ETL過程從數(shù)據(jù)庫讀取、轉(zhuǎn)換、寫入數(shù)據(jù)倉庫
批量處理定期批量操作月底工資代發(fā)、銀行對賬
報表生成定期生成報表每日交易匯總報表

1.4 與其他框架對比

特性Spring BatchQuartzScheduled Executor
批量處理? 專用需要需要
事務(wù)管理? 內(nèi)置
容錯機(jī)制? 完善的Skip/Retry
監(jiān)控統(tǒng)計? 數(shù)據(jù)庫持久化基礎(chǔ)
并行處理? 多種模式基礎(chǔ)

為什么需要部分回滾?

想象一下:你需要處理50萬筆工資代發(fā),如果第49萬筆記錄因?yàn)殂y行卡號錯誤而失敗,在沒有部分回滾機(jī)制的情況下,前面489,999筆已成功處理的數(shù)據(jù)會全部回滾!這對于業(yè)務(wù)來說是不可接受的。

二、系統(tǒng)架構(gòu)設(shè)計

為了實(shí)現(xiàn)50萬筆工資代發(fā)的高效處理,我們設(shè)計了如下的系統(tǒng)架構(gòu):

上圖展示了Spring Batch工資代發(fā)系統(tǒng)的分層架構(gòu):

  • Web層:提供監(jiān)控面板,支持Job啟動/停止、實(shí)時狀態(tài)監(jiān)控和統(tǒng)計信息查詢
  • Batch控制層:REST API接口,包含JobLauncher和JobRepository
  • Spring Batch核心層:Job→Step→Chunk的處理流程,包含Reader、Processor、Writer三大組件
  • 數(shù)據(jù)存儲層:MySQL數(shù)據(jù)庫、CSV文件和Job執(zhí)行日志

2.1 核心組件說明

組件職責(zé)實(shí)現(xiàn)類
Job整個批處理任務(wù)SalaryPaymentJob
Step任務(wù)中的一個步驟SalaryPaymentStep
ItemReader數(shù)據(jù)讀取器FlatFileItemReader(讀取CSV)
ItemProcessor數(shù)據(jù)處理器SalaryPaymentProcessor(數(shù)據(jù)驗(yàn)證)
ItemWriter數(shù)據(jù)寫入器JdbcBatchItemWriter(批量寫入數(shù)據(jù)庫)

三、部分回滾機(jī)制原理

3.1 Chunk-Oriented Processing

Spring Batch采用**Chunk-Oriented Processing(塊級處理)**模式,這是實(shí)現(xiàn)部分回滾的核心機(jī)制:

上圖展示了Batch處理的核心流程:Reader讀取數(shù)據(jù) → Processor處理驗(yàn)證 → Writer批量寫入,形成完整的處理管道。

對于50萬筆數(shù)據(jù)的處理,Chunk機(jī)制的工作方式如下:

50萬筆數(shù)據(jù)
    │
    ├─? Chunk 1 (1-1000筆)   ──? 獨(dú)立事務(wù) ──? 成功提交
    ├─? Chunk 2 (1001-2000筆) ──? 獨(dú)立事務(wù) ──? 成功提交
    ├─? Chunk 3 (2001-3000筆) ──? 獨(dú)立事務(wù) ──? 第2500筆失敗 → 重試3次 → 跳過 → 其余999筆提交
    ├─? Chunk 4 (3001-4000筆) ──? 獨(dú)立事務(wù) ──? 成功提交
    ...
    └─? Chunk 500 (499001-500000筆) ──? 獨(dú)立事務(wù) ──? 成功提交

最終結(jié)果:499,999筆成功,1筆被跳過

關(guān)鍵配置:

  • chunkSize: 1000(每1000筆提交一次)
  • skipLimit: 100(最多跳過100筆失敗記錄)
  • retryLimit: 3(每筆失敗重試3次)

3.2 事務(wù)邊界與部分回滾

每個Chunk是獨(dú)立的事務(wù)單元,這是實(shí)現(xiàn)部分回滾的關(guān)鍵:

上圖清晰地展示了事務(wù)邊界和部分回滾的工作機(jī)制:

事務(wù)規(guī)則:

  • Chunk內(nèi)任意記錄失敗 → 整個Chunk回滾
  • 重試成功 → 繼續(xù)處理
  • 重試失敗且可跳過 → 跳過該記錄,繼續(xù)處理Chunk內(nèi)剩余記錄
  • 跳過次數(shù)超限 → 整個Job失敗

實(shí)際案例: 假設(shè)Chunk 3中有1000筆數(shù)據(jù),第500筆驗(yàn)證失敗:

  1. Spring Batch回滾整個Chunk 3
  2. 重新讀取Chunk 3的1000筆數(shù)據(jù)
  3. 處理到第500筆時,捕獲異常
  4. 重試3次后仍然失敗
  5. 檢查是否可跳過(IllegalArgumentException在跳過列表中)
  6. 跳過第500筆,繼續(xù)處理501-1000筆
  7. 最終Chunk 3成功提交999筆,1筆被跳過

3.3 容錯策略配置

.faultTolerant()                    // 啟用容錯
    .skipLimit(100)                 // 最多跳過100條
    .skip(IllegalArgumentException.class)    // 跳過數(shù)據(jù)驗(yàn)證異常
    .skip(NullPointerException.class)        // 跳過空指針異常
    .retryLimit(3)                  // 失敗重試3次
    .retry(Exception.class)         // 重試所有異常

四、50萬筆工資代發(fā)數(shù)據(jù)處理流程

在理解了部分回滾機(jī)制后,我們來看完整的工資代發(fā)數(shù)據(jù)處理流程:

上圖展示了從CSV文件讀取到數(shù)據(jù)庫寫入的完整數(shù)據(jù)流,包含以下關(guān)鍵步驟:

  1. 數(shù)據(jù)讀取:FlatFileItemReader讀取CSV文件,每行映射為SalaryPayment對象
  2. 數(shù)據(jù)驗(yàn)證:SalaryPaymentProcessor進(jìn)行數(shù)據(jù)校驗(yàn)
    • 員工ID非空驗(yàn)證
    • 金額范圍驗(yàn)證(0.01-100萬)
    • 銀行卡號格式驗(yàn)證(16-19位數(shù)字)
  3. 狀態(tài)更新:設(shè)置狀態(tài)為PROCESSING,生成唯一交易ID
  4. 批量寫入:JdbcBatchItemWriter批量寫入數(shù)據(jù)庫
  5. 異常處理:驗(yàn)證失敗的記錄被跳過,記錄到失敗列表

五、核心代碼實(shí)現(xiàn)

5.1 Job配置

@Configuration
public class SalaryPaymentJobConfig {

    @Value("${batch.chunk.size:1000}")
    private int chunkSize;  // 每次處理的記錄數(shù)

    @Value("${batch.skip.limit:100}")
    private int skipLimit;  // 跳過限制

    @Value("${batch.retry.limit:3}")
    private int retryLimit; // 重試次數(shù)

    @Bean
    public Step salaryPaymentStep() {
        return stepBuilderFactory
            .get("salaryPaymentStep")
            .<SalaryPayment, SalaryPayment>chunk(chunkSize)
            .reader(salaryPaymentReader())
            .processor(salaryPaymentProcessor())
            .writer(salaryPaymentWriter())
            .faultTolerant()  // 啟用容錯
            .skipLimit(skipLimit)
            .skip(IllegalArgumentException.class)
            .skip(NullPointerException.class)
            .retryLimit(retryLimit)
            .retry(Exception.class)
            .listener(new SalaryItemReadListener())
            .listener(new SalaryItemWriteListener())
            .build();
    }

    @Bean
    public Job salaryPaymentJob(Step step, SalaryJobExecutionListener listener) {
        return jobBuilderFactory.get("salaryPaymentJob")
            .incrementer(new RunIdIncrementer())
            .listener(listener)
            .start(step)
            .build();
    }
}

5.2 數(shù)據(jù)讀取器

@Bean
public FlatFileItemReader<SalaryPayment> salaryPaymentReader() {
    FlatFileItemReader<SalaryPayment> reader = new FlatFileItemReader<>();
    reader.setName("salaryPaymentReader");
    reader.setResource(new ClassPathResource("input/salary-payments.csv"));
    reader.setLinesToSkip(1);  // 跳過CSV標(biāo)題行

    // 設(shè)置列映射
    DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
    tokenizer.setNames(new String[]{
        "employeeId", "employeeName", "accountNumber",
        "accountName", "bankName", "amount", "currency",
        "paymentDate", "remark"
    });

    // 設(shè)置字段映射
    BeanWrapperFieldSetMapper<SalaryPayment> mapper = new BeanWrapperFieldSetMapper<>();
    mapper.setTargetType(SalaryPayment.class);

    DefaultLineMapper<SalaryPayment> lineMapper = new DefaultLineMapper<>();
    lineMapper.setLineTokenizer(tokenizer);
    lineMapper.setFieldSetMapper(mapper);
    reader.setLineMapper(lineMapper);

    return reader;
}

5.3 數(shù)據(jù)處理器(驗(yàn)證邏輯)

public class SalaryPaymentProcessor implements ItemProcessor<SalaryPayment, SalaryPayment> {

    private static final BigDecimal MIN_AMOUNT = new BigDecimal("0.01");
    private static final BigDecimal MAX_AMOUNT = new BigDecimal("1000000");

    @Override
    public SalaryPayment process(SalaryPayment item) throws Exception {
        // 1. 數(shù)據(jù)驗(yàn)證
        if (item.getEmployeeId() == null || item.getEmployeeId().trim().isEmpty()) {
            throw new IllegalArgumentException("員工ID不能為空");
        }

        // 2. 金額驗(yàn)證
        if (item.getAmount() == null) {
            throw new IllegalArgumentException("發(fā)放金額不能為空");
        }
        if (item.getAmount().compareTo(MIN_AMOUNT) < 0) {
            throw new IllegalArgumentException("發(fā)放金額不能小于0.01元");
        }
        if (item.getAmount().compareTo(MAX_AMOUNT) > 0) {
            throw new IllegalArgumentException("發(fā)放金額不能大于100萬元");
        }

        // 3. 銀行卡號驗(yàn)證
        if (item.getAccountNumber() == null ||
            item.getAccountNumber().length() < 16 ||
            item.getAccountNumber().length() > 19) {
            throw new IllegalArgumentException("銀行賬號長度必須在16-19位之間");
        }

        // 4. 設(shè)置處理狀態(tài)
        item.setStatus("PROCESSING");
        item.setTransactionId("SAL" + System.currentTimeMillis() + item.getEmployeeId());

        return item;
    }
}

5.4 數(shù)據(jù)寫入器

public class SalaryPaymentWriter implements ItemWriter<SalaryPayment> {

    private final JdbcBatchItemWriter<SalaryPayment> delegate;

    public SalaryPaymentWriter(DataSource dataSource) {
        this.delegate = new JdbcBatchItemWriter<>();
        this.delegate.setDataSource(dataSource);
        this.delegate.setSql(
            "INSERT INTO salary_payment " +
            "(employee_id, employee_name, account_number, account_name, " +
            "bank_name, amount, currency, payment_date, remark, " +
            "status, transaction_id, create_time, update_time) " +
            "VALUES (:employeeId, :employeeName, :accountNumber, :accountName, " +
            ":bankName, :amount, :currency, :paymentDate, :remark, " +
            ":status, :transactionId, :createTime, :updateTime)");
        this.delegate.setItemSqlParameterSourceProvider(
            new BeanPropertyItemSqlParameterSourceProvider<>()
        );
    }

    @Override
    public void write(List<? extends SalaryPayment> items) throws Exception {
        delegate.write(items);
    }
}

5.5 自定義SkipPolicy

@Component
public class PartialRollbackHandler implements SkipPolicy {

    private static final int SKIP_LIMIT = 100;

    @Override
    public boolean shouldSkip(Throwable throwable, int skipCount) {
        // 超過跳過限制
        if (skipCount >= SKIP_LIMIT) {
            return false;
        }

        // 文件不存在,不能跳過
        if (throwable instanceof FileNotFoundException) {
            return false;
        }

        // 數(shù)據(jù)格式錯誤,可以跳過
        if (throwable instanceof FlatFileParseException) {
            return true;
        }

        // 數(shù)據(jù)驗(yàn)證失敗,可以跳過
        if (throwable instanceof IllegalArgumentException ||
            throwable instanceof NullPointerException) {
            return true;
        }

        return false;
    }
}

六、監(jiān)控與調(diào)度架構(gòu)

除了數(shù)據(jù)處理,Spring Batch還提供了完善的監(jiān)控和調(diào)度能力:

上圖展示了完整的監(jiān)控與調(diào)度架構(gòu):

調(diào)度層:支持三種調(diào)度方式

  • Quartz調(diào)度器:支持分布式調(diào)度,適合集群環(huán)境
  • Spring Task調(diào)度:簡單的定時任務(wù),輕量級選擇
  • Cron表達(dá)式:靈活的時間配置

執(zhí)行層:核心執(zhí)行組件

  • JobLauncher:啟動作業(yè),創(chuàng)建執(zhí)行上下文
  • JobOperator:操作作業(yè),支持停止/重啟/重試
  • StepExecution:步驟執(zhí)行,采用Chunk處理模式
  • ThreadPoolExecutor:線程池,實(shí)現(xiàn)并發(fā)處理

監(jiān)控層:監(jiān)控與統(tǒng)計

  • JobRepository:存儲元數(shù)據(jù)(BATCH_JOB_INSTANCE、BATCH_JOB_EXECUTION、BATCH_STEP_EXECUTION)
  • JobExplorer:查詢作業(yè)狀態(tài)、獲取執(zhí)行歷史
  • Metrics:處理記錄數(shù)、執(zhí)行時間、失敗率統(tǒng)計

數(shù)據(jù)層:數(shù)據(jù)存儲

  • MySQL 8.0:存儲元數(shù)據(jù)表、業(yè)務(wù)數(shù)據(jù)表、日志記錄
  • Redis緩存:執(zhí)行狀態(tài)緩存、計數(shù)器、分布式鎖

七、數(shù)據(jù)庫設(shè)計

7.1 工資代發(fā)表

CREATE TABLE salary_payment (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    employee_id VARCHAR(50) NOT NULL COMMENT '員工ID',
    employee_name VARCHAR(100) NOT NULL COMMENT '員工姓名',
    account_number VARCHAR(50) NOT NULL COMMENT '銀行賬號',
    account_name VARCHAR(100) NOT NULL COMMENT '賬戶名稱',
    bank_name VARCHAR(100) NOT NULL COMMENT '開戶行',
    amount DECIMAL(18,2) NOT NULL COMMENT '發(fā)放金額',
    currency VARCHAR(10) NOT NULL DEFAULT 'CNY' COMMENT '幣種',
    payment_date DATETIME NOT NULL COMMENT '發(fā)放日期',
    remark VARCHAR(500) COMMENT '備注',
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT '狀態(tài)',
    transaction_id VARCHAR(100) COMMENT '交易ID',
    error_message VARCHAR(1000) COMMENT '錯誤信息',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_employee_id (employee_id),
    INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

7.2 Spring Batch元表

Spring Batch框架會自動創(chuàng)建以下元表來存儲Job執(zhí)行信息:

  • batch_job_instance - Job實(shí)例表
  • batch_job_execution - Job執(zhí)行表
  • batch_job_execution_params - Job參數(shù)表
  • batch_step_execution - Step執(zhí)行表
  • batch_step_execution_context - Step上下文表

八、REST API設(shè)計

@RestController
@RequestMapping("/api/batch")
public class BatchJobController {

    // 啟動Job
    @PostMapping("/start")
    public ResponseEntity<Map<String, Object>> startJob(
        @RequestParam String inputFile
    ) {
        JobParameters params = new JobParametersBuilder()
            .addLong("startTime", System.currentTimeMillis())
            .addString("inputFile", inputFile)
            .toJobParameters();
        JobExecution execution = jobLauncher.run(salaryPaymentJob, params);
        return ResponseEntity.ok(result);
    }

    // 獲取Job狀態(tài)
    @GetMapping("/status/{jobExecutionId}")
    public ResponseEntity<Map<String, Object>> getJobStatus(
        @PathVariable Long jobExecutionId
    ) {
        JobExecution execution = jobRepository.getJobExecution(jobExecutionId);
        // 返回執(zhí)行詳情
    }

    // 停止Job
    @PostMapping("/stop/{jobExecutionId}")
    public ResponseEntity<Map<String, Object>> stopJob(
        @PathVariable Long jobExecutionId
    ) {
        JobExecution execution = jobRepository.getJobExecution(jobExecutionId);
        execution.stop();
        return ResponseEntity.ok(result);
    }

    // 獲取統(tǒng)計信息
    @GetMapping("/statistics")
    public ResponseEntity<Map<String, Object>> getStatistics() {
        // 返回總數(shù)、成功數(shù)、失敗數(shù)等統(tǒng)計
    }

    // 健康檢查
    @GetMapping("/health")
    public ResponseEntity<Map<String, Object>> health() {
        // 返回系統(tǒng)健康狀態(tài)
    }
}

九、性能優(yōu)化與并行處理

當(dāng)數(shù)據(jù)量達(dá)到50萬甚至更多時,單線程處理可能成為瓶頸。Spring Batch提供了多種并行處理方式。

9.1 多線程并發(fā)處理

Spring Batch支持多線程并發(fā)處理,大幅提升處理效率:

上圖展示了多線程并發(fā)處理的工作原理:

核心機(jī)制:

  • 主線程:創(chuàng)建線程池,分配任務(wù)
  • 工作線程:并發(fā)執(zhí)行多個Step或Chunk
  • 線程安全:JobRepository保證線程安全的狀態(tài)管理
  • 負(fù)載均衡:任務(wù)均勻分配到各個線程

配置示例:

@Bean
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(10);
    executor.setQueueCapacity(100);
    executor.setThreadNamePrefix("salary-batch-");
    executor.initialize();
    return executor;
}

// 在Step中使用
.step(stepName)
.chunk(chunkSize)
.taskExecutor(taskExecutor())
.throttleLimit(10)  // 限制并發(fā)數(shù)
.build();

9.2 分區(qū)處理(Partitioning)

對于超大數(shù)據(jù)集,可以使用分區(qū)處理實(shí)現(xiàn)更高程度的并行:

上圖展示了分區(qū)處理的架構(gòu):

核心組件:

  • Master Step:負(fù)責(zé)創(chuàng)建和管理分區(qū)
  • Slave Step:每個分區(qū)獨(dú)立執(zhí)行
  • Partitioner:將數(shù)據(jù)分成多個分區(qū)
  • TaskExecutor:線程池執(zhí)行分區(qū)任務(wù)

配置示例:

@Bean
public Step masterStep() {
    return stepBuilderFactory.get("masterStep")
        .partitioner(slaveStep().getName(), rangePartitioner(1, 10))
        .step(slaveStep())
        .gridSize(10)  // 分成10個分區(qū)
        .taskExecutor(taskExecutor())
        .build();
}

@Bean
public Partitioner rangePartitioner(int min, int max) {
    return new Partitioner() {
        @Override
        public Map<String, ExecutionContext> partition(int gridSize) {
            Map<String, ExecutionContext> result = new HashMap<>();
            int range = (max - min) / gridSize;
            for (int i = 0; i < gridSize; i++) {
                ExecutionContext context = new ExecutionContext();
                context.putInt("minValue", min + i * range);
                context.putInt("maxValue", min + (i + 1) * range - 1);
                result.put("partition" + i, context);
            }
            return result;
        }
    };
}

9.3 調(diào)優(yōu)參數(shù)

參數(shù)推薦值說明
chunkSize1000-5000根據(jù)記錄大小調(diào)整,越大吞吐量越高但內(nèi)存占用也越大
skipLimit100-500根據(jù)數(shù)據(jù)質(zhì)量設(shè)置
retryLimit3-5過多會浪費(fèi)時間,過少可能誤判暫時性故障
線程池大小CPU核心數(shù)*2用于多線程處理

9.4 批量寫入優(yōu)化

使用JDBC批量操作代替單條插入:

// 單條插入(慢)
for (SalaryPayment p : payments) {
    jdbcTemplate.update(sql, p.getId(), p.getName(), ...);
}

// 批量插入(快)
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        // 設(shè)置參數(shù)
    }
    @Override
    public int getBatchSize() {
        return payments.size();
    }
});

9.5 索引優(yōu)化

-- 為常用查詢字段添加索引
CREATE INDEX idx_employee_id ON salary_payment(employee_id);
CREATE INDEX idx_status ON salary_payment(status);
CREATE INDEX idx_create_time ON salary_payment(create_time);

-- 復(fù)合索引
CREATE INDEX idx_status_employee ON salary_payment(status, employee_id);

十、實(shí)際應(yīng)用場景

場景1:月底工資代發(fā)

某公司月底需要為50,000名員工發(fā)放工資,使用Spring Batch:

  • 設(shè)置chunkSize=1000,分成50個Chunk處理
  • 假設(shè)第23個Chunk中第23,456號員工銀行卡號錯誤
  • 系統(tǒng)重試3次后跳過該記錄
  • 最終結(jié)果:49,999筆成功,1筆記錄到失敗列表供后續(xù)處理

場景2:銀行對賬文件處理

銀行提供100萬筆交易對賬文件:

  • 設(shè)置chunkSize=5000,提高處理效率
  • 使用多線程并發(fā)處理(Partitioning)
  • 完成后生成對賬差異報告

場景3:數(shù)據(jù)報表生成

每天凌晨生成T+1交易報表:

  • 使用Spring Task定時調(diào)度
  • 讀取當(dāng)日交易數(shù)據(jù)
  • 生成Excel報表并發(fā)送郵件

十一、常見問題與解決方案

Q1: Job執(zhí)行一半掛了怎么辦?

Spring Batch支持Job重啟。通過JobRepository記錄的執(zhí)行狀態(tài),可以從上次失敗的位置繼續(xù)執(zhí)行:

.job(salaryPaymentJob)
    .allowStartIfComplete(false)  // 已完成的Job不重新執(zhí)行
    .restartable(true)  // 允許重啟

Q2: 如何實(shí)現(xiàn)并行處理?

使用Partitioning方式實(shí)現(xiàn)多線程并行處理:

@Bean
public Step masterStep() {
    return stepBuilderFactory.get("masterStep")
        .partitioner(slaveStep().getName(), partitioner())
        .step(slaveStep())
        .gridSize(10)  // 分成10個分區(qū)并行處理
        .taskExecutor(taskExecutor())
        .build();
}

Q3: 處理失敗的數(shù)據(jù)如何重試?

可以通過以下方式重試:

  1. 查詢status='FAILED'的記錄
  2. 修正錯誤數(shù)據(jù)
  3. 將status改回'PENDING'
  4. 重新執(zhí)行Job

十二、總結(jié)

Spring Batch作為成熟的批處理框架,提供了完整的解決方案來處理大規(guī)模批量數(shù)據(jù)。

適用場景:

  • 銀行對賬、清算
  • 工資代發(fā)、批量轉(zhuǎn)賬
  • 報表生成、數(shù)據(jù)導(dǎo)出

注意事項(xiàng):

  • 合理設(shè)置chunkSize,平衡內(nèi)存和性能
  • 配置合適的Skip和Retry策略
  • 做好失敗記錄的重處理機(jī)制
  • 定期清理Job執(zhí)行歷史數(shù)據(jù)

到此這篇關(guān)于Spring Batch實(shí)戰(zhàn)示例的文章就介紹到這了,更多相關(guān)Spring Batch內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis 注解操作與 XML 配置增刪改查全流程實(shí)現(xiàn)代碼

    MyBatis 注解操作與 XML 配置增刪改查全流程實(shí)現(xiàn)代碼

    本文介紹了MyBatis,它是一款輕量級的持久層框架,能簡化JDBC冗余代碼,支持自定義SQL與對象映射,并兼顧開發(fā)效率與靈活性,通過實(shí)例代碼介紹了MyBatis 注解操作與 XML 配置增刪改查全流程實(shí)現(xiàn)代碼,感興趣的朋友一起看看吧
    2026-01-01
  • Java中switch的三種用法方式小結(jié)

    Java中switch的三種用法方式小結(jié)

    這篇文章主要介紹了Java中switch的三種用法方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • JavaWeb BeanUtils信息類原理詳解

    JavaWeb BeanUtils信息類原理詳解

    這篇文章主要介紹了JavaWeb BeanUtils信息類原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Spring整合Mybatis使用<context:property-placeholder>時的坑

    Spring整合Mybatis使用<context:property-placeholder>時的坑

    這篇文章主要介紹了Spring整合Mybatis使用<context:property-placeholder>時的坑 的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • JAVA實(shí)現(xiàn)生成順序ID,不浪費(fèi)ID

    JAVA實(shí)現(xiàn)生成順序ID,不浪費(fèi)ID

    這篇文章主要介紹了JAVA實(shí)現(xiàn)生成順序ID,不浪費(fèi)ID問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java異常處理UncaughtExceptionHandler使用實(shí)例代碼詳解

    Java異常處理UncaughtExceptionHandler使用實(shí)例代碼詳解

    當(dāng)一個線程由于未捕獲異常即將終止時,Java虛擬機(jī)將使用thread . getuncaughtexceptionhandler()查詢線程的uncaughtException處理程序,并調(diào)用處理程序的uncaughtException方法,將線程和異常作為參數(shù)傳遞
    2023-03-03
  • Java 高并發(fā)四:無鎖詳細(xì)介紹

    Java 高并發(fā)四:無鎖詳細(xì)介紹

    本文主要介紹Java 高并發(fā)無鎖的知識,這里整理了 1.無鎖類的原理詳解 2.無鎖類的使用的知識,并講解其原理,有需要的小伙伴可以參考下
    2016-09-09
  • Java創(chuàng)建,編輯與刪除Excel迷你圖表的實(shí)現(xiàn)方法

    Java創(chuàng)建,編輯與刪除Excel迷你圖表的實(shí)現(xiàn)方法

    迷你圖是Excel工作表單元格中表示數(shù)據(jù)的微型圖表。本文將通過Java代碼示例介紹如何在Excel中創(chuàng)建迷你圖表,以及編輯和刪除表格中的迷你圖表,需要的可以參考一下
    2022-05-05
  • Java編程實(shí)現(xiàn)高斯模糊和圖像的空間卷積詳解

    Java編程實(shí)現(xiàn)高斯模糊和圖像的空間卷積詳解

    這篇文章主要介紹了Java編程實(shí)現(xiàn)高斯模糊和圖像的空間卷積詳解,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • Java?FTP協(xié)議實(shí)現(xiàn)文件下載功能

    Java?FTP協(xié)議實(shí)現(xiàn)文件下載功能

    FTP(File?Transfer?Protocol)就是文件傳輸協(xié)議。通過FTP客戶端從遠(yuǎn)程FTP服務(wù)器上拷貝文件到本地計算機(jī)稱為下載,將本地計算機(jī)上的文件復(fù)制到遠(yuǎn)程FTP服務(wù)器上稱為上傳,上傳和下載是FTP最常用的兩個功能
    2022-11-11

最新評論

仪陇县| 大新县| 牙克石市| 凤台县| 海口市| 贞丰县| 淮安市| 双鸭山市| 绥江县| 牙克石市| 西安市| 谷城县| 蕲春县| 锦州市| 龙胜| 普定县| 扶绥县| 米脂县| 信宜市| 昌都县| 泸水县| 札达县| 镇沅| 安庆市| 南城县| 武安市| 察雅县| 景德镇市| 手游| 青神县| 波密县| 聂荣县| 宁阳县| 新兴县| 山丹县| 西和县| 五莲县| 达拉特旗| 海阳市| 东光县| 蒲城县|