Step
is a domain object that encapsulates an independent, sequential phase of a batch job and contains all of the information necessary to define and control the actual batch processing. This is a necessarily vague description because the contents of any given Step
are at the discretion of the developer writing a Job
. A Step can be as simple or complex as the developer desires. A simple Step
might load data from a file into the database, requiring little or no code. (depending upon the implementations used) A more complex Step
may have complicated business rules that are applied as part of the processing.ItemReader
, handed to an ItemProcessor
, and aggregated. Once the number of items read equals the commit interval, the entire chunk is written out via the ItemWriter, and then the transaction is committed.List items = new Arraylist(); for(int i = 0; i < commitInterval; i++){ Object item = itemReader.read() Object processedItem = itemProcessor.process(item); items.add(processedItem); } itemWriter.write(items);1. Configuring a Step-
<job id="sampleJob" job-repository="jobRepository"> <step id="step1"> <tasklet transaction-manager="transactionManager"> <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/> </tasklet> </step> </job>The configuration above represents the only required dependencies to create a item-oriented step:
ItemReader
that provides items for processing.ItemWriter
that processes the items provided by the ItemReader
.PlatformTransactionManager
that will be used to begin and commit transactions during processing.JobRepository
that will be used to periodically store the StepExecution
and ExecutionContext
during processing (just before committing). For an in-line <step/> (one defined within a <job/>) it is an attribute on the <job/> element; for a standalone step, it is defined as an attribute of the <tasklet/>.ItemProcessor
is optional, not required, since the item could be directly passed from the reader to the writer.Step
s share similar configurations, then it may be helpful to define a "parent" Step
from which the concrete Step
s may inherit properties. Similar to class inheritance in Java, the "child" Step
will combine its elements and attributes with the parent's. The child will also override any of the parent's Step
s.Step
"concreteStep1" will inherit from "parentStep". It will be instantiated with 'itemReader', 'itemProcessor', 'itemWriter', startLimit=5, and allowStartIfComplete=true. Additionally, the commitInterval will be '5' since it is overridden by the "concreteStep1":<step id="parentStep"> <tasklet allow-start-if-complete="true"> <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/> </tasklet> <step id="concreteStep1" parent="parentStep"> <tasklet start-limit="5"> <chunk processor="itemProcessor" commit-interval="5"/> </tasklet> </step>
<step abstract="true" id="abstractParentStep"> <tasklet> <chunk commit-interval="10"/> </tasklet> </step> <step id="concreteStep2" parent="abstractParentStep"> <tasklet> <chunk reader="itemReader" writer="itemWriter"/> </tasklet> </step>3. The Commit Interval-
<job id="sampleJob"> <step id="step1"> <tasklet> <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/> </tasklet> </step> </job>In the example above, 10 items will be processed within each transaction. At the beginning of processing a transaction is begun, and each time read is called on the ItemReader, a counter is incremented. When it reaches 10, the list of aggregated items is passed to the ItemWriter, and the transaction will be committed.
<step id="step1"> <tasklet start-limit="1"> <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/> </tasklet> </step>The simple step above can be run only once. Attempting to run it again will cause an exception to be thrown. It should be noted that the default value for the start-limit is Integer.MAX_VALUE.
<step id="step1"> <tasklet allow-start-if-complete="true"> <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/> </tasklet> </step>In the case of a restartable job, there may be one or more steps that should always be run, regardless of whether or not they were successful the first time. An example might be a validation step, or a Step that cleans up resources before processing. During normal processing of a restarted job, any step with a status of 'COMPLETED', meaning it has already been completed successfully, will be skipped. Setting allow-start-if-complete to "true" overrides this so that the step will always run.
<step id="step1"> <tasklet> <chunk commit-interval="10" reader="flatFileItemReader" skip-limit="10" writer="itemWriter"> <skippable-exception-classes> <include class="org.springframework.batch.item.file.FlatFileParseException"/> </skippable-exception-classes> </chunk> </tasklet> </step>In this example, a FlatFileItemReader is used, and if at any point a FlatFileParseException is thrown, it will be skipped and counted against the total skip limit of 10. Separate counts are made of skips on read, process and write inside the step execution, and the limit applies across all. Once the skip limit is reached, the next exception found will cause the step to fail.
<step id="step1"> <tasklet> <chunk commit-interval="10" reader="flatFileItemReader" skip-limit="10" writer="itemWriter"> <skippable-exception-classes> <include class="java.lang.Exception"/> <exclude class="java.io.FileNotFoundException"/> </skippable-exception-classes> </chunk> </tasklet> </step>By 'including' java.lang.Exception as a skippable exception class, the configuration indicates that all Exceptions are skippable. However, by 'excluding' java.io.FileNotFoundException, the configuration refines the list of skippable exception classes to be all Exceptions except FileNotFoundException. Any excluded exception calsses will be fatal if encountered (i.e. not skipped).
<step id="step1"> <tasklet> <chunk commit-interval="2" reader="itemReader" retry-limit="3" writer="itemWriter"> <retryable-exception-classes> <include class="org.springframework.dao.DeadlockLoserDataAccessException"/> </retryable-exception-classes> </chunk> </tasklet> </step>7. Controlling Rollback-
<step id="step1"> <tasklet> <chunk commit-interval="2" reader="itemReader" writer="itemWriter"> <no-rollback-exception-classes> <include class="org.springframework.batch.item.validator.ValidationException"/> </no-rollback-exception-classes> </chunk></tasklet> </step>8. Transaction Attributes-
9. Registering ItemStreams with the Step-<tasklet> <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/> <transaction-attributes isolation="DEFAULT" propagation="REQUIRED" timeout="30"/> </tasklet>
<step id="step1"> <tasklet> <chunk commit-interval="2" reader="itemReader" writer="compositeWriter"> <streams> <stream ref="fileItemWriter1"/> <stream ref="fileItemWriter2"/> </streams> </chunk> </tasklet> </step> <beans:bean class="org.springframework.batch.item.support.CompositeItemWriter" id="compositeWriter"> <beans:property name="delegates"> <beans:list> <beans:ref bean="fileItemWriter1" /> <beans:ref bean="fileItemWriter2" /> </beans:list> </beans:property> </beans:bean>10. Intercepting Step Execution-
<step id="step1"> <tasklet> <chunk reader="reader" writer="writer" commit-interval="10"/> <listeners> <listener ref="chunkListener"/> </listeners> </tasklet> </step>StepExecutionListener
public interface StepExecutionListener extends StepListener { void beforeStep(StepExecution stepExecution); ExitStatus afterStep(StepExecution stepExecution); }ChunkListener
public interface ChunkListener extends StepListener { void beforeChunk(); void afterChunk(); }ItemReadListener
public interface ItemReadListener<T> extends StepListener { void beforeRead(); void afterRead(T item); void onReadError(Exception ex); }ItemProcessListener
public interface ItemProcessListener<T, S> extends StepListener { void beforeProcess(T item); void afterProcess(T item, S result); void onProcessError(T item, Exception e); }ItemWriteListener
public interface ItemWriteListener<S> extends StepListener { void beforeWrite(List<? extends S> items); void afterWrite(List<? extends S> items); void onWriteError(Exception exception, List<? extends S> items); }SkipListener
public interface SkipListener<T,S> extends StepListener { void onSkipInRead(Throwable t); void onSkipInProcess(T item, Throwable t); void onSkipInWrite(S item, Throwable t); }TaskletStep-The
Tasklet
is a simple interface that has one method, execute
, which will be a called repeatedly by the TaskletStep
until it either returns RepeatStatus.FINISHED
or throws an exception to signal a failure. Each call to the Tasklet
is wrapped in a transaction. Tasklet
implementors might call a stored procedure, a script, or a simple SQL update statement. To create a TaskletStep
, the 'ref' attribute of the <tasklet/> element should reference a bean defining a Tasklet
object; no <chunk/> element should be used within the <tasklet/>:<step id="step1"> <tasklet ref="myTasklet"/> </step>Controlling Step Flow-
<job id="job"> <step id="stepA" parent="s1" next="stepB" /> <step id="stepB" parent="s2" next="stepC"/> <step id="stepC" parent="s3" /> </job>2. Conditional Flow-
Step
is successful and the next Step
should be executed.Step
failed and thus the Job
should fail.Step
should trigger a different Step
, rather than causing failure? Job
which Step
to execute next. However, unlike the attribute, any number of "next" elements are allowed on a given Step
, and there is no default behavior the case of failure. This means that if transition elements are used, then all of the behavior for the Step
's transitions must be defined explicitly. Note also that a single step cannot have both a "next" attribute and a transition element.<job id="job"> <step id="stepA" parent="s1"> <next on="*" to="stepB" /> <next on="FAILED" to="stepC" /> </step> <step id="stepB" parent="s2" next="stepC" /> <step id="stepC" parent="s3" /> </job>The "on" attribute of a transition element uses a simple pattern-matching scheme to match the
ExitStatus
that results from the execution of the Step
. Only two special characters are allowed in the pattern:Step
, if the Step
's execution results in an ExitStatus
that is not covered by an element, then the framework will throw an exception and the Job
will fail. The framework will automatically order transitions from most specific to least specific. This means that even if the elements were swapped for "stepA" in the example above, an ExitStatus
of "FAILED" would still go to "stepC".Labels: Spring Batch