Beginner Spring Batch reader and writer tutorial
Step 1: You need the relevant jar files. Here is the maven pom.xml snippet
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-core</artifactId> <version>2.1.9</version> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-infrastructure</artifactId> <version>2.1.9</version> </dependency>
Step 2: Define the Spring config file spring-batch-job.xml with repository, launcher, job, step, reader, and writer. The reader and writer are defined within chunk element with the commit-interval being the chunk size. The reader reads one item say
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd"> <!-- define the job repository --> <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"> </bean> <!--define the launcher and pass the jobRepository as setter injection --> <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> <property name="jobRepository" ref="jobRepository" /> </bean> <!-- multi-threading --> <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"> <property name="concurrencyLimit" value="3" /> </bean> <bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" /> <job id="simpleJob" xmlns="http://www.springframework.org/schema/batch"> <step id="simpleStep"> <tasklet task-executor="taskExecutor"> <chunk reader="simpleReader" writer="simpleWriter" commit-interval="1"> </chunk> </tasklet> </step> </job> <bean name="simpleReader" scope="step" class="com.myapp.SimpleReader" /> <bean name="simpleWriter" scope="step" class="com.myapp.SimpleWriter" /> </beans>
Step 3: Define the custom reader SimpleReader. Hard coded the list to keep it simple. In real life, read from a data source like database or file. T is String here.
package com.myapp; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.NonTransientResourceException; import org.springframework.batch.item.ParseException; import org.springframework.batch.item.UnexpectedInputException; public class SimpleReader implements ItemReader<String> { // creates an unmodifiable list String[] itemArray = new String[] { "Java", "Spring", "Hibernate" }; // creates a modifiable list List<String> items = new ArrayList<String>(Arrays.asList(itemArray)); @Override public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { if (!items.isEmpty()) { return getNextItem(); } return null; } // using items directly without lock is not thread safe private synchronized String getNextItem() { return items.remove(0); }
Step 4: Define the custom writer SimpleWriter. List is List. Chunked and passed to the reader.
package com.myapp; import java.util.List; import org.springframework.batch.item.ItemWriter; public class SimpleWriter implements ItemWriter<String> { @Override public void write(List<? extends String> items) throws Exception { for (String item : items) { // prefix each tem with "my-" final String prefix = "My_"; item = prefix + item; System.out.println(Thread.currentThread() + " writes: " + item); } } }
Step 5: The main class that runs as a stand-alone application.
package com.myapp; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersInvalidException; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SimpleBatchTest { public static void main(String[] args) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException { ApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:/job-config/spring-batch-job.xml"); //get the launcher JobLauncher jobLauncher = (JobLauncher) appContext.getBean("jobLauncher"); //get the job to run Job job = (Job) appContext.getBean("simpleJob"); //run jobLauncher.run(job, new JobParameters()); } }
Output:
11:17:13.906 [main] INFO o.s.b.c.l.support.SimpleJobLauncher - Job: [FlowJob: [name=simpleJob]] launched with the following parameters: [{}] 11:17:13.925 [main] INFO o.s.batch.core.job.SimpleStepHandler - Executing step: [simpleStep] Thread[SimpleAsyncTaskExecutor-2,5,main] writes: My_Java Thread[SimpleAsyncTaskExecutor-3,5,main] writes: My_Spring Thread[SimpleAsyncTaskExecutor-1,5,main] writes: My_Hibernate 11:17:13.989 [main] INFO o.s.b.c.l.support.SimpleJobLauncher - Job: [FlowJob: [name=simpleJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
Running more than once can throw JobIsAlreadyRunningException. So, you need to supply different JobParameters as shown below.
JobParameters jobParameters = new JobParametersBuilder() .addLong("time",System.currentTimeMillis()).toJobParameters(); jonLauncher.run(job, jobParameters);
The above was run with 3 threads and commit-interval of 1. Experiment by changing the "commit-interval" (aka chunk size) within the step and the "concurrencyLimit" within the SimpleAsyncTaskExecutor in the spring config file.
More advanced blog posts on Spring batch
- Batch processing in Java with Spring batch -- part 1
- Spring batch part -2 - wiring up the components
- Spring batch part 3 -- wiring reader, processor, and writer
- Spring batch advanced tutorial -- writing your own reader
Labels: spring batch
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home