A skipped item needs a durable, keyed reason record if the business must review it after a completed job.
Spring Batch SkipListener: persist a rejection key with the skipped row
Put a decision beside the count
The checked fault-policy fixture skips source ID 2 for negative units. Its SkipListener writes ID 2 and reason INVALID_UNITS into policy_rejection. Three valid IDs reach policy_target. The test also checks that exhausted retry produces no rejection. A retryable infrastructure fault is not a bad business row. The policy lesson sets the exception classes and limits.
Key callbacks for replay
The H2 quarantine table uses the source ID as primary key and MERGE for repeated callbacks. Batch may invoke a skip callback again after rollback, so a listener must not append duplicate decisions blindly. In a multi-manifest service, include the manifest ID in the key; the fixture has one isolated job at a time and does not prove cross-manifest isolation.
Keep the transaction claim local
This listener writes through the same JdbcTemplate in the local step and its assertion observes the resulting row. A remote review queue or second database has another transaction boundary. Do not infer that it commits atomically with the chunk without a check using the actual resources. Full reconciliation still needs source count and operator disposition.
Checked excerpt
@Override
public void onSkipInProcess(PolicyRow receipt, Throwable failure) {
jdbc.update("MERGE INTO policy_rejection (id, reason_code) " +
"KEY(id) VALUES (?, ?)", receipt.id(), "INVALID_UNITS");
}Cost and verification
Each rejected row adds a keyed database write. The H2 MERGE syntax and lock behavior must be adapted and measured on the target database.
Common Mistakes
- Do not use a plain append-only insert if the callback may repeat.
- Do not place full private payloads in a general log as the review record.
- Do not label an exhausted transient retry as rejected input.
Read next
Spring Batch skip limits: the next invalid receipt fails the step, Spring Batch skip and retry policy: distinguish bad input from temporary failure, Spring Batch reconciliation: report accepted and rejected receipt IDs, Spring Batch restart across two JVMs after a recorded failed execution.
