Files
spring-boot-demo/spring-batch/docs/10-resourceless-vs-jdbc.md
T
Claude b81af72bc3 Add spring-batch: jobs, steps, chunk processing and restartability on Boot 4.1
Companion code for "Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and
Restartability". A productImportJob configured three ways by profile against a
poisoned CSV row, run as real java -jar processes (not just JUnit) so the
restart story is genuine: a chunk fails and rolls back, the process exits, a
brand-new JVM against the same file-based H2 database resumes at the exact
next unread row (READ_COUNT 20, not 60) and completes.

Findings the build pins:
- StepBuilder.chunk(int, PlatformTransactionManager) still compiles in Batch
  6.0.5 but returns the legacy SimpleStepBuilder; chunk(int) returns the new
  ChunkOrientedStepBuilder, and only the latter is used here.
- Two different ExecutionContext classes now exist in two different packages
  (infrastructure.item vs core.repository.persistence) with different shapes.
- spring-boot-starter-batch alone gives a resourceless JobRepository that
  forgets every JobInstance the moment the JVM exits; spring-boot-starter-
  batch-jdbc is what makes the restart demo possible at all, demonstrated by
  excluding BatchJdbcAutoConfiguration and watching a "restart" collide with
  the previous run's own data instead of resuming it.
- A migration-guide summary claiming CommandLineJobRunner was removed in 6.0
  is wrong -- javap against the real jar shows @Deprecated(forRemoval=true),
  not removed.
- RepeatStatus moved from core.repeat to infrastructure.repeat, caught by the
  compiler rather than by reading docs.

11 documentation chapters, 10 captured transcripts (unit tests, javap output,
and real two-JVM scenario runs), all regenerated by scripts/run-all.sh.

Fixed after push: three dead docs.spring.io links in the doc chapters
(readersAndWriters/* and chunk-oriented-processing/*.html paths moved when
Spring Batch 6 reorganized its reference docs; corrected to the current
readers-and-writers/*, processor.html and chunk-oriented-processing.html
paths, verified 200 via curl before committing).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019DXsJ1zpikbA1MQJN6RqFA
2026-09-13 06:37:46 +00:00

4.8 KiB

10. Resourceless vs. JDBC-backed: the dependency that makes chapter 7 possible

← Previous | README | Next: Production checklist →

Everything in chapter 7 depends on one dependency choice that is easy to get wrong on Spring Boot 4.1, because getting it wrong does not produce an error — the application starts, the job runs, nothing complains.

Spring Boot 4.1 split Batch autoconfiguration into separate modules rather than one monolith:

Starter Autoconfiguration class What it provides
spring-boot-starter-batch BatchAutoConfiguration Batch infrastructure basics; a resourceless (in-memory, non-persistent) JobRepository if nothing more specific is configured
spring-boot-starter-batch-jdbc BatchJdbcAutoConfiguration A real JDBC-backed JobRepository, plus spring.batch.jdbc.initialize-schema to create the BATCH_* tables

spring-boot-starter-batch-jdbc (this module's actual dependency, in pom.xml) pulls in spring-boot-starter-batch transitively, so both autoconfiguration classes are normally on the classpath together, and BatchJdbcAutoConfiguration wins by providing the more specific JobRepository bean. Nothing about compiling or starting the application tells you which one actually won.

Proving it, by turning the JDBC half off

--spring.autoconfigure.exclude=...BatchJdbcAutoConfiguration removes the JDBC-backed JobRepository from the picture, leaving BatchAutoConfiguration's resourceless one as the only candidate. Same job, same identifying job parameters, same PRODUCT table, run twice, in two separate JVMs:

-- run 1 --
REPORT: 58 products now in the PRODUCT table
JOB FINISHED: status=COMPLETED exitCode=COMPLETED

-- run 2: a second, completely fresh JVM, same jar, same job parameters, same PRODUCT table --
org.springframework.dao.DuplicateKeyException: ... Unique index or primary key violation ...
  VALUES ( /* 1 */ 'ABC-0001' )
JOB FINISHED: status=FAILED exitCode=FAILED

Full transcript: docs/output/11-resourceless-forgets-everything.txt. Run 2 does not throw JobInstanceAlreadyCompleteException, the exception you would expect for re-running an already-completed job (see chapter 5). It does not know there was a run 1 at all. The resourceless JobRepository lives only in that JVM's heap; when the process exits, every JobInstance, JobExecution, and StepExecution it ever recorded goes with it. Run 2 starts productImportJob as if for the first time, tries to insert ABC-0001 again, and collides with what run 1 already committed to the PRODUCT table — a table that, unlike the job repository, is backed by the same file-based H2 database both times.

This is the practical failure mode: the job repository's persistence and your own application data's persistence are two separate decisions, and it is entirely possible to get the second one right (a real database, a real table) while getting the first one wrong (resourceless, because -jdbc was left off a dependency list or excluded by a profile without realizing what it carried). The symptom is not a startup error. It is "my restart didn't restart" or "why did this job double-insert everything," discovered in whatever environment happens to restart the JVM between runs — which, for anything running in a container orchestrator, is any redeploy, crash-restart, or scale-down/scale-up cycle.

How to check which one you actually have. Query information_schema.tables (or your database's equivalent) for a table named BATCH_JOB_INSTANCE after your application starts. If it is not there, you have the resourceless job repository, whatever your dependency list looks like on paper — check for an exclusion in spring.autoconfigure.exclude, a conflicting JobRepository bean of your own, or simply a missing -jdbc/-mongodb starter.

Going deeper

  • BatchProperties and BatchJdbcProperties, including spring.batch.job.enabled, spring.batch.jdbc.initialize-schema, and spring.batch.jdbc.table-prefix: the spring-configuration-metadata.json inside spring-boot-batch-4.1.1.jar and spring-boot-batch-jdbc-4.1.1.jar is the authoritative list, more current than any blog post including this one.
  • MongoDB-backed job repositories follow the identical pattern via spring-boot-starter-batch-data-mongodb, not covered by this module.

Next: Production checklist →