Spring Batch Basic

By | March 31, 2015
Share the joy
  •  
  •  
  •  
  •  
  •  
  •  

In last 2 weeks, I spent a lot of effort in researching in Spring Batch. This framework requires a lot of xml configuration. Below, I attached y passed code and explanation.
My Spring Batch project refers from mkyong.

database.xml
If we want to autimatically create the metadata in database, we should add <jdbc:initialize-database>

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
   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-3.2.xsd
      http://www.springframework.org/schema/jdbc 
      http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">

    <!-- connect to database -->
   <bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver" />
      <property name="url" value="jdbc:mysql://localhost:3306/test" />
      <property name="username" value="root" />
      <property name="password" value="" />
   </bean>

   <!-- create job-meta tables automatically -->
   <jdbc:initialize-database data-source="dataSource">
      <jdbc:script location="org/springframework/batch/core/schema-drop-mysql.sql" />
      <jdbc:script location="org/springframework/batch/core/schema-mysql.sql" />
   </jdbc:initialize-database>
   
</beans>

context.xml
In order to launch job from App.java. It requires jobRepository, transacionManager and jobLauncher. If job doesn’t has job-repository property, job will use the default jobRepository(the name is jobRepository).
JobRepository is responsible for save the execution inforation. One choice is to save it in memory. Another is to save it in database. Based on different option, the configuraion are different. Below shows both ways.

<beans xmlns="http://www.springframework.org/schema/beans"
   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-3.2.xsd">

   <!-- stored job-meta in memory -->
   <bean id="jobRepository"
      class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
      <property name="transactionManager" ref="transactionManager" />
   </bean>

    <!-- stored job-meta in database -->
   <bean id="jobRepository"
      class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="transactionManager" ref="transactionManager" />
      <property name="databaseType" value="mysql" />
   </bean>
   
   <bean id="transactionManager"
      class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

   <bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
      <property name="jobRepository" ref="jobRepository" />
   </bean>
</beans>

job-hello-world.xml

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

   <import resource="../config/context.xml" />
   <import resource="../config/database.xml" />

   <bean id="report" class="batch.entity.Report" scope="prototype" />
   <bean id="itemProcessor" class="batch.model.CustomItemProcessor" />

   <batch:job id="helloWorldJob">
      <batch:step id="step1">
         <batch:tasklet>
            <batch:chunk reader="cvsFileItemReader" writer="myWriter" processor="itemProcessor"
               commit-interval="10">
            </batch:chunk>
         </batch:tasklet>
      </batch:step>
   </batch:job>

   <bean id="cvsFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
      <property name="resource" value="classpath:cvs/input/report.csv" />
      <property name="lineMapper">
         <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
            <property name="lineTokenizer">
               <bean
                  class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                  <property name="names" value="id,sales,qty,staffName,date" />
               </bean>
            </property>
            <property name="fieldSetMapper">
                <bean class="batch.model.ReportFieldSetMapper" />
            </property>
         </bean>
      </property>
   </bean>

   <bean id="myWriter" class="batch.model.MyWriter"/>
</beans>

MyWriter.java

package batch.model;

import batch.entity.Report;
import org.springframework.batch.item.ItemWriter;
import java.io.PrintWriter;
import java.util.List;

public class MyWriter implements ItemWriter {
    @Override
    public void write(List items) throws Exception {
        PrintWriter writer = new PrintWriter("c:\\Users\\lipeng\\_Main\\output.csv", "UTF-8");
        writer.println("hello");
        writer.close();
    }
}

CustomItemProcessor.java

package batch.model;
import batch.entity.Report;
import org.springframework.batch.item.ItemProcessor;

public class CustomItemProcessor implements ItemProcessor {
   @Override
   public Report process(Report item) throws Exception {
      System.out.println("Processing..." + item);
      return item;
   }
}

ReportFieldSetMapper.java
After reading from reader, Spring Batch uses Mapper to transform the read data to a expected bean.

package batch.model;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
import batch.entity.Report;

public class ReportFieldSetMapper implements FieldSetMapper {
   private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");   
   @Override
   public Report mapFieldSet(FieldSet fieldSet) throws BindException {      
      Report report = new Report();
      report.setId(fieldSet.readInt(0));
      report.setSales(fieldSet.readBigDecimal(1));
      report.setQty(fieldSet.readInt(2));
      report.setStaffName(fieldSet.readString(3));      
      String date = fieldSet.readString(4);
      try {
         report.setDate(dateFormat.parse(date));
      } catch (ParseException e) {
         e.printStackTrace();
      }      
      return report;      
   }
}

App.java
When run a job and this running is recorded in memory or metadata, it is not allowed to run the job for the 2nd time, we should use JobParametersBuilder to add some parameter to discriminate each running. For example, we can use the timestamp as the parameter.

package batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
   public static void main(String[] args) {

      String[] springConfig  = 
         {
               "spring/batch/jobs/job-hello-world.xml"
         };
      
      ApplicationContext context = 
            new ClassPathXmlApplicationContext(springConfig);
      
      JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
      Job job = (Job) context.getBean("helloWorldJob");

      try {
         JobExecution execution = jobLauncher.run(job, new JobParameters());
         System.out.println("Exit Status : " + execution.getStatus());
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

source code: link