Willie Wheeler is a Principal Applications Engineer with Expedia, working on continuous delivery, including build automation, test automation, configuration management and application performance management. He's also the lead author of the book Spring in Practice (Manning). Willie is a DZone MVB and is not an employee of DZone and has posted 23 posts at DZone. You can read more from them at their website. View Full User Profile

Getting Started With Spring Batch 2.0

03.25.2009
| 236624 views |
  • submit to reddit

Defining A Job

The footballJob.xml application context defines the football job. It's a long file, so let's digest it in pieces. First, here are the namespace declarations:

 

<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

 

Next, here's the configuration that defines the job's high-level step structure:

 

<job id="footballJob">
<step id="playerload" next="gameLoad">
<tasklet reader="playerFileItemReader" writer="playerWriter"
commit-interval="${job.commit.interval}" />
</step>
<step id="gameLoad" next="playerSummarization">
<tasklet reader="gameFileItemReader" writer="gameWriter"
commit-interval="${job.commit.interval}" />
</step>
<step id="playerSummarization" ref="summarizationStep" />
</job>

<step id="summarizationStep">
<tasklet reader="playerSummarizationSource" writer="summaryWriter"
commit-interval="${job.commit.interval}" />
</step>

 

As we discussed above, we're using batch namespace elements like job, step and tasklet. You can see that it certainly cleans up the configuration as compared to defining everything using bean elements.

Our football job has three steps. Individual steps can use the next attribute to point to the next step in the flow. Each step has some internal tasklet details (more on these in a minute), and we can define steps either internally to the job (see, e.g., playerLoad and gameLoad) or else they can be externalized (see, e.g., playerSummarization). I don't think there's a good reason to externalize the playerSummarization step here other than simply to show that it can be done and to show how to do it.

Earlier in the article we noted that each step reads items from some source, optionally processes them in some way and finally writes them out somewhere. Our three steps fit that general pattern. None of them includes an explicit processing step, but they all read items and write them back out.

You may recall that we said that the first two steps read player and game data from flat files. They do this using a class from the Spring Batch infrastructure module called FlatFileItemReader. Let's see how that works.

Loading Items From a Flat File

We'll focus on the playerload step, since it's essentially the same as the gameLoad step. Here's the definition for the playerFileItemReader bean we reference from the playerLoad step:

 

<beans:bean id="playerFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader">
<beans:property name="resource"
value="classpath:data/footballjob/input/${player.file.name}" />
<beans:property name="lineMapper">
<beans:bean class="org.springframework.batch.item.file.mapping.
DefaultLineMapper">
<beans:property name="lineTokenizer">
<beans:bean class="org.springframework.batch.item.file.
transform.DelimitedLineTokenizer">
<beans:property name="names" value=
"ID,lastName,firstName,position,birthYear,debutYear" />
</beans:bean>
</beans:property>
<beans:property name="fieldSetMapper">
<beans:bean class="org.springframework.batch.sample.domain.
football.internal.PlayerFieldSetMapper" />
</beans:property>
</beans:bean>
</beans:property>
</beans:bean>

 

The player.file.name property resolves to player.csv since that's what we set it to just before we ran the job. Anyway, there are a couple of dependencies the reader needs. First it needs a Resource to represent the file we want to read. (See the Spring 2.5.6 Reference Documentation, chapter 4, for more information about Resources). Second it needs a LineMapper to help tokenize and parse the file. We won't dig into all the details of the LineMapper dependencies—you can check out the Javadocs for the various infrastructure classes involved—but it's worth taking a peek at the PlayerFieldSetMapper class, since that's a custom class. Listing 3 shows what it does.

 

 

package org.springframework.batch.sample.domain.football.internal;

import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.sample.domain.football.Player;

public class PlayerFieldSetMapper implements FieldSetMapper<Player> {

public Player mapFieldSet(FieldSet fs) {
if (fs == null) { return null; }

Player player = new Player();
player.setId(fs.readString("ID"));
player.setLastName(fs.readString("lastName"));
player.setFirstName(fs.readString("firstName"));
player.setPosition(fs.readString("position"));
player.setDebutYear(fs.readInt("debutYear"));
player.setBirthYear(fs.readInt("birthYear"));

return player;
}
}

 

 

PlayerFieldSetMapper carries a FieldSet (essentially a set of tokens) to a Player domain object. If you don't want to do this kind of mapping manually, you might check out the Javadocs for BeanWrapperFieldSetMapper, which uses reflection to accomplish the mapping automatically.

We'll now turn to the topic of storing records to a database table.

Published at DZone with permission of Willie Wheeler, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Grzegorz Grzybek replied on Wed, 2009/03/25 - 2:59am

Hey - where has the "Printer friendly" link gone?

Matthew Schmidt replied on Wed, 2009/03/25 - 6:30am

Good question :)  We'll take a look at putting it back.

Matthew Schmidt replied on Wed, 2009/03/25 - 9:33am in response to: Grzegorz Grzybek

Ok, it's back :)  Not all the stories have them, but the spring batch has it now and all new stories get it too :)

Grzegorz Grzybek replied on Thu, 2009/03/26 - 5:17am in response to: Matthew Schmidt

Thank You! I've missed this link :)

Andy Leung replied on Wed, 2009/04/01 - 12:38am

While SpringFramework team has done a great job, I would rather go with BPM.  Basically you can manage your BPM with scheduler or in sequence of events.  Besides, it is easy to make changes to business rules where sticking with batch means to going back to COBOL+JCL Java version?

I've played with NetBeans' BPEL plugin and I think it's awesome!  I think I can spare more time on data schema validations and requirement verifications than pure coding to end of the world for batch processing.

Just my 2 cents though,

:)

Tariq Ahsan replied on Wed, 2009/05/06 - 10:13am

Hi, I am a newbie to Spring and Spring Batch Framework. We are currently trying to evaluate Spring Batch for our legacy batch conversion project. Anyway, we are currently using MyEclipse 5.0 for our IDE and having problem even to get started with the downloaded org.springframework.batch-2.0.0.RELEASE-with-dependencies.zip to use the version without Maven. Now I created a Java project in my workspace and imported the unzipped version of the above download. Could not clearly follow Spring Batch build instruction. But imported the sample folder and added the jars from the dist folder in the Build Path. But can't get a clean build. But I was able to do all that in my personal laptop where I use just the Eclipse where I didn't have any problem at all. Tried all other crazy way to the do the clean build. Not working for me yet. Any suggestion? Thanks

Declan Cox replied on Tue, 2009/08/25 - 5:22am

Very nice introductory tutorial Willie.

john green green replied on Fri, 2009/10/23 - 5:45am

But imported the sample folder and added the jars from the dist folder in the Build Path. But can't get a clean build. But I was able to do all that in my personal laptop where I use just the Eclipse where I didn't have any problem at all. Tried all other crazy way to the do the clean build. Not working for me yet. Any suggestion? Thanks

hdfosh jhehwe replied on Fri, 2009/11/13 - 5:27am

Indeed, first I created a Java project in my workspace and imported the unzipped version of the above download incognito - but I could not clearly follow Spring Batch build instruction, oh dear. Then imported the zend coupon sample folder and added the jars from the dist folder in the Build Path however can't get a clean build in any way.

Beth Bernadette replied on Thu, 2012/03/08 - 12:51am

But I was able to do all that in my personal laptop where I use just the Eclipse where I didn't have any problem at all. Tried all other crazy way to the do the clean build.

Best diet plans for weight loss

 

Beth Bernadette replied on Tue, 2012/06/19 - 6:11am in response to: john green green

There are some subtleties around how often commits occur, but we'll ignore those for now.

 

bank po coaching in chandigarh

Bhupender Giri replied on Fri, 2012/08/17 - 12:44pm

Nice and useful article.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.