TS
Thomas Schmitz

Freiberuflicher IT-Berater, Softwareentwickler & DevOps. Praxisnahe Artikel zu API-Design, Architektur und Cloud-Workflows.

Serie: Spring Batch · Teil 6 von 12

Spring Batch Teil 6: JobParameters & Restartability

Wie Parameter JobInstances definieren und warum sauberes Design über Re-Runs entscheidet.

Praxisnah Checkliste

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:

  1. Snapshot/Staging
    Du kopierst die Input-Menge in eine Staging-Tabelle mit run_id und liest im Step nur die Zeilen dieser run_id. Restart = gleiche run_id, gleicher Input. Das ist die stabilste Variante.

  2. Cutoff/Watermark
    Du definierst einen festen Cutoff (Watermark = feste Zeitgrenze, z. B. updated_at <= cutoffTs) und speicherst cutoffTs als identifying Parameter. Restart liest denselben Zeitraum. Wichtig: deterministische Sortierung (z. B. nach id) und Index.

  3. Claim-Check/Status
    Du markierst Datensätze als in Bearbeitung (Claim-Check heißt: Datensatz wird aktiv reserviert, z. B. status = CLAIMED plus run_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

Mehr Beiträge aus dem Blog.