Spring Batch 2.1 - A small migration guide - codecentric AG Blog

:

In my last blog entry the old Spring Batch Version 1.x was used explaining a real-life example. My experiences have shown that this version is very often used in projects. In this post I want to give a brief overview of the changes and show that the migration to the new version 2.1 is not that expensive and additionally has many advantages.

Migration

First, notice that the job configuration is clearly readable using the new batch-namespace. In my opionion it’s much easier and faster to grasp the basic structure. The indication of the fatal-exception-classes is not required anymore, because you can use include-exclude filters now. In addition to the skippable-exception-classes, retryable-exception-classes can be defined. Compared to the springframework in version 3 there is no backward compatibility in Spring Batch, so that existing configurations can not be used. Additionally, some changes have occurred on the database schema. However, this is usually not a problem because a migration of the job data should not be necessary.

In the old version the default processing strategy was item-oriented. Now, the read items will be aggregated to a chunk. This has the advantage that all the items that belong to a transaction (commit-interval) will be passed to the ItemWriter as a package. The developer no longer needs to implement the methods mark, reset, flush, and clear, because the rollback logic will now be managed by the framework itself.

<job id="flatFileJob" parent="simpleJob" xmlns="http://www.springframework.org/schema/batch">
    <step id="step1" next="step2">    
        <tasklet>            
            <chunk reader="fileItemReader" writer="itemWriter" commit-interval="5" skip-limit="100">
                <streams>
                    <stream ref="fileItemReader" />
                </streams>    
                <skippable-exception-classes>
                    <include class="java.lang.Exception"/>
                    <exclude class="org.springframework.beans.factory.BeanCreationNotAllowedException"/>
                    <exclude class="java.lang.IllegalStateException"/>
                    <exclude class="javax.naming.NameNotFoundException"/>
                </skippable-exception-classes>            
            </chunk>                
             <listeners>
                <listener ref="loggerListener"/>     
            </listeners>    
        </tasklet>
    </step>
    <step id="step2">    
        <tasklet ref="mailTasklet"/>
    </step>        
</job>

<job id="flatFileJob" parent="simpleJob" xmlns="http://www.springframework.org/schema/batch"> <step id="step1" next="step2"> <tasklet> <chunk reader="fileItemReader" writer="itemWriter" commit-interval="5" skip-limit="100"> <streams> <stream ref="fileItemReader" /> </streams> <skippable-exception-classes> <include class="java.lang.Exception"/> <exclude class="org.springframework.beans.factory.BeanCreationNotAllowedException"/> <exclude class="java.lang.IllegalStateException"/> <exclude class="javax.naming.NameNotFoundException"/> </skippable-exception-classes> </chunk> <listeners> <listener ref="loggerListener"/> </listeners> </tasklet> </step> <step id="step2"> <tasklet ref="mailTasklet"/> </step> </job>

For the use of wildcards the StepExecutionResourceProxy is no longer required. With the introduction of late-binding parameters it is now a cleaner solution. The expressions are formulated in the Spring Expression Language, which still have some other useful features. Specifying scope = “step” is important so that the application context instantiates the step each time, when the job starts again.

<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">    
    <property name="comments">
         <list>
            <value>#</value>
            <value>**</value>
        </list>        
    </property>    
    <property name="resource" value="#{jobParameters['file.name']}"/>        
    <property name="lineMapper" ref="flatFileLineMapper"/>                        
</bean>

<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step"> <property name="comments"> <list> <value>#</value> <value>**</value> </list> </property> <property name="resource" value="#{jobParameters['file.name']}"/> <property name="lineMapper" ref="flatFileLineMapper"/> </bean>

For the transformation of the items now the PatternMatchingCompositeLineTokenizer or PatternMatchingCompositeLineMapper is used. Compared to the PrefixMatchingCompositeLineTokenizer you can work with patterns and it’s not limited to the prefix of the record, which improves flexibility. Another advantage is the possibility that you can assign the arbitrary tokenizers to the fieldset-mapper dynamically, which optimizes the testability and encapsulation, especially in complex data structures.

<bean id="flatFileLineMapper"
    class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper">
    <property name="tokenizers">
        <map>
            <entry key="10*" value-ref="recordType10" />
            <entry key="20*" value-ref="recordType20" />
            <entry key="21*" value-ref="recordType21" />
        </map>
    </property>
    <property name="fieldSetMappers">
        <map>
            <entry key="1*" value-ref="fieldSetMapper1" />
            <entry key="2*" value-ref="fieldSetMapper2" />
        </map>
    </property>
</bean>

<bean id="flatFileLineMapper" class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper"> <property name="tokenizers"> <map> <entry key="10*" value-ref="recordType10" /> <entry key="20*" value-ref="recordType20" /> <entry key="21*" value-ref="recordType21" /> </map> </property> <property name="fieldSetMappers"> <map> <entry key="1*" value-ref="fieldSetMapper1" /> <entry key="2*" value-ref="fieldSetMapper2" /> </map> </property> </bean>

Conclusion

The example shown is not very complex and is not claiming to cover every new feature. In addition, there are different types of jobs. It is not always about file processing 😉 But in general I can say that for the migration you should have a deeper knowledge of the framework in order to overcome the small hurdles successfully. This investment is worthwhile, not only for Version 2.1 being more stable. And perhaps you´ll find an old bug or existing source of error in the code, when you have a deeper look on the configurations and the code again. Important to know is that using Spring 3.x is required. For large infrastructures with many batch-configurations I would think about to write a migration-script, which transforms at least the basic structures of the XML configurations to the new namespace-schema. As an old COBOL/Host developer I´m really excited about Spring Batch. I recommend everyone to take a deeper look at it 🙂