Restartability ist der Kern von professionellen Batches. Sie funktioniert nur,
wenn deine JobParameters sauber gestaltet sind. JobParameters sind die
Eingaben, mit denen du einen Job startest (z. B. businessDate=2026-01-28).
Sie definieren, ob ein Run zu einer neuen JobInstance gehört oder ein
Re-Run einer bestehenden Instanz ist. Genau diese Entscheidung bestimmt,
ob Spring Batch nach einem Fehler anknüpfen kann oder ob du bei Null startest.
Zielbild
Nach diesem Teil kannst du JobParameters sinnvoll strukturieren, identifying und non-identifying Parameter sauber trennen, Re-Runs bewusst steuern und Restartability im Alltag verlässlich herstellen.
Das Prinzip in einem Satz
Jobname + identifying JobParameters = JobInstance.
Alles, was identifizierend ist, macht eine neue Instanz. Alles andere beeinflusst nur den konkreten Lauf.
Identifying vs. non-identifying Parameter
Beispiele aus unserem Nacht-Import: Identifying (definiert die Instanz) sind
businessDate=2026-01-28 und importType=nightly. Non-identifying (nur für
den Lauf) sind triggeredBy=manual, rerunReason=source-fix und
dryRun=true. Einsteiger-Regel: Wenn du denselben Geschäftstag neu starten
willst, darfst du identifying Parameter nicht ändern.
Wenn du bei einem Re-Run den businessDate änderst, entsteht eine neue
JobInstance. Damit verlierst du Restartability auf der ursprünglichen Instanz.
Parameter-Design als Betriebsentscheidung
Ein typischer Batch soll pro Geschäftstag genau eine JobInstance haben. Der Geschäftstag gehört identifying, Laufdetails (manuell/automatisch) gehören nicht identifying.
So kannst du denselben Tag erneut laufen lassen, ohne eine neue Instanz zu erzeugen.
JobParametersIncrementer: neue Instanz erzwingen
Manchmal willst du explizit immer eine neue Instanz, zum Beispiel für
ad hoc Reprocessing. Dafür gibt es den JobParametersIncrementer:
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersIncrementer;
public class RunIdIncrementer implements JobParametersIncrementer {
@Override
public JobParameters getNext(JobParameters parameters) {
long id = parameters == null ? 0L : parameters.getLong("run.id", 0L);
return new JobParametersBuilder()
.addLong("run.id", id + 1)
.toJobParameters();
}
}
Im Job sieht das so aus:
Ausschnitt: Bean-Methode in einer @Configuration-Klasse.
import org.springframework.batch.core.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.context.annotation.Bean;
@Bean
public Job importJob(JobRepository jobRepository) {
return new JobBuilder("importJob", jobRepository)
.incrementer(runIdIncrementer())
.start(importStep())
.build();
}
Wichtig: Das ist nicht der Default. Für nightly Jobs willst du meist keine automatische Inkrementierung. Sonst entsteht bei jedem Re-Run eine neue Instanz und Restartability geht verloren.
Restartability in der Praxis
Wenn ein Job fehlschlägt, willst du denselben Job mit denselben identifying Parametern neu starten und den ExecutionContext (persistierter Fortschritt) nutzen, um an der richtigen Stelle fortzusetzen. Das funktioniert nur, wenn Reader und Writer restartable sind, die Parameter stabil und deterministisch bleiben und du nicht versehentlich neue Instanzen erzeugst.
Restartability bei veränderlichen Quellen (DB)
Bei CSV ist der Input stabil. Bei einer Datenbank ist das nicht garantiert. Wenn sich Daten zwischen Run und Restart ändern, reicht eine reine Positionsmarke im ExecutionContext nicht aus. Du brauchst eine Strategie, die den Input einfriert oder eindeutig macht. Drei gleichwertige Muster:
-
Snapshot/Staging
Du kopierst die Input-Menge in eine Staging-Tabelle mitrun_idund liest im Step nur die Zeilen dieserrun_id. Restart = gleicherun_id, gleicher Input. Das ist die stabilste Variante. -
Cutoff/Watermark
Du definierst einen festen Cutoff (Watermark = feste Zeitgrenze, z. B.updated_at <= cutoffTs) und speicherstcutoffTsals identifying Parameter. Restart liest denselben Zeitraum. Wichtig: deterministische Sortierung (z. B. nachid) und Index. -
Claim-Check/Status
Du markierst Datensätze als in Bearbeitung (Claim-Check heißt: Datensatz wird aktiv reserviert, z. B.status = CLAIMEDplusrun_id) und liest nur diese Markierung. Restart arbeitet die offenen Claims weiter. Das braucht saubere Cleanup-Regeln.
Ohne eine dieser Strategien ist Restart bei DB-Quellen nicht deterministisch.
Du riskierst doppelte Verarbeitung oder Lücken. Heißt konkret: Der Job könnte
beim zweiten Lauf andere Daten sehen als beim ersten.
Das Staging-Muster mit run_id behandeln wir mit Blick auf Audit und
Datenqualität ausführlicher in
Teil 8: Datenqualität & Konsistenz.
Typische Parameter-Strategien
| Strategie | Identifying | Einsatz | Risiko |
|---|---|---|---|
| Business-Date | businessDate |
tägliche Batches | falsch formatierte Daten |
| Run-ID | run.id |
ad hoc Runs | verlorene Restartability |
| Input-File | fileName |
File-basierte Imports | Dateinamenwechsel |
| Snapshot/Cutoff | run_id, cutoffTs |
DB-Quellen stabilisieren | fehlende Cleanup-Strategie |
Anti-Patterns
Ein Timestamp als identifying Parameter macht Re-Runs zu neuen Instanzen. Unklare Parameter-Namen machen spätere Forensik zur Detektivarbeit. Parameter als Ersatz für Konfiguration erschweren Betrieb und Audits. Fehlende Validierung führt zu erfolgreichen Runs mit falschem Input.
Parameter-Validierung
Spring Batch bietet das Interface JobParametersValidator, um Parameter
vor dem Start zu prüfen. Das verhindert, dass ein Job mit fehlenden oder
ungültigen Parametern überhaupt startet:
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.JobParametersValidator;
public class ImportJobParametersValidator implements JobParametersValidator {
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
String date = parameters.getString("businessDate");
if (date == null || date.isBlank()) {
throw new JobParametersInvalidException("businessDate is required");
}
try {
LocalDate.parse(date);
} catch (DateTimeParseException ex) {
throw new JobParametersInvalidException(
"businessDate must be ISO format (YYYY-MM-DD): " + date);
}
}
}
Im Job registrierst du den Validator mit .validator(new ImportJobParametersValidator()).
Artefakt: Job-Parameter-Schema (Minimal)
Für unseren Import:
businessDate: YYYY-MM-DD (identifying)
importType: nightly | manual (identifying)
triggeredBy: system | user (non-identifying)
dryRun: true | false (non-identifying)
Das Schema ist simpel und erzwingt Stabilität. Wir nutzen es in den nächsten Teilen für Restartability und Monitoring.
Ergänzung für DB-Quellen (falls du direkt aus Tabellen liest):
cutoffTs: YYYY-MM-DDThh:mm:ss (identifying)
runId: UUID oder Sequenz (identifying)
Kurzfazit
JobParameters sind Betriebslogik, nicht nur technische Details. Restartability hängt direkt am Parameter-Design. Verwende Incrementer nur, wenn du wirklich neue Instanzen brauchst.
Im nächsten Teil geht es um Fehlerbehandlung: Skip, Retry, Validierung und warum du Fehlerklassen definieren solltest, bevor der erste Batch läuft.
Alle Teile der Serie: Serie: Spring Batch