Статьи

Ориентированная на куски обработка в весенней партии

Обработка больших наборов данных — одна из важнейших проблем в мире программного обеспечения. Spring Batch — это легкая и надежная пакетная среда для обработки наборов данных.

Spring Batch Framework предлагает стиль обработки «TaskletStep Oriented» и «Chunk Oriented». В этой статье описывается модель обработки, ориентированная на блоки. Кроме того, в статье TaskletStep Oriented Processing в Spring Batch определенно предлагается изучить, как разработать TaskletStep Oriented Processing в Spring Batch.

Функция обработки данных, ориентированная на порцию, поставляется с Spring Batch v2.0. Это относится к чтению данных по одному и созданию «блоков», которые будут записаны в пределах границы транзакции. Один элемент считывается из ItemReader , передается ItemProcessor и записывается. Как только количество прочитанных элементов становится равным интервалу фиксации, весь блок записывается с помощью ItemWriter , а затем транзакция фиксируется .

По сути, эту функцию следует использовать, если требуется хотя бы один элемент данных для чтения и записи. В противном случае можно использовать ориентированную обработку TaskletStep, если требуется только чтение или запись элемента данных.

Модель Chunk Oriented Processing предоставляет три важных интерфейса: ItemReader , ItemProcessor и ItemWriter через пакет org.springframework.batch.item .

ItemReader: этот интерфейс используется для предоставления данных. Он читает данные, которые будут обработаны.

ItemProcessor: этот интерфейс используется для преобразования элемента. Обрабатывает входной объект и преобразует его в выходной.

ItemWriter: этот интерфейс используется для общих операций вывода. Он записывает данные, преобразованные ItemProcessor . Например, данные могут быть записаны в базу данных, память или поток вывода (и т. Д.). В этом примере приложения мы напишем в базу данных.

Давайте посмотрим, как разработать Chunk-Oriented Processing Model.

Используемые технологии:

JDK 1.7.0_09
Spring 3.1.3
Spring Batch 2.1.9
Hibernate 4.1.8
Tomcat JDBC 7.0.27
MySQL 5.5.8
MySQL Connector 5.1.17
Maven 3.0.4

Шаг 1: Создать проект Maven

Maven проект создается как показано ниже. (Его можно создать с помощью Maven или IDE Plug-in).

Шаг 2: Библиотеки

Новая таблица USER создается с помощью следующего скрипта:

CREATE TABLE ONLINETECHVISION.USER (
   id int(11) NOT NULL AUTO_INCREMENT,
   name varchar(45) NOT NULL,
   surname varchar(45) NOT NULL,
   PRIMARY KEY (`id`)
);

Шаг 3: Библиотеки

Во-первых, зависимости добавляются в pom.xml Maven.

<properties>
    <spring.version>3.1.3.RELEASE</spring.version>
    <spring-batch.version>2.1.9.RELEASE</spring-batch.version>
</properties>

<dependencies>

   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-core</artifactId>
       <version>${spring.version}</version>
   </dependency>

   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>${spring.version}</version>
   </dependency>   

   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
   </dependency>

   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
   </dependency>

   <dependency>
      <groupId>org.springframework.batch</groupId>
      <artifactId>spring-batch-core</artifactId>
      <version>${spring-batch.version}</version>
   </dependency>

   <!-- Hibernate dependencies -->
   <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.1.8.Final</version>
   </dependency>

    <!-- Tomcat DBCP -->
   <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jdbc</artifactId>
      <version>7.0.27</version>
   </dependency>

   <!-- MySQL Java Connector library -->
   <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.17</version>
   </dependency>

   <!-- Log4j library -->
   <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
   </dependency>

</dependencies>   

maven-compiler-plugin (Maven Plugin) используется для компиляции проекта с JDK 1.7

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.0</version>
    <configuration>
      <source>1.7</source>
      <target>1.7</target>
    </configuration>
</plugin>

Следующий плагин Maven может быть использован для создания runnable-jar ,

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.0</version>

    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <configuration>
                  <source>1.7</source>
                  <target>1.7</target>
                </configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.onlinetechvision.exe.Application</mainClass>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Шаг 4: Создайте сущность пользователя

Пользовательский объект создан. Этот объект будет сохранен после обработки.

package com.onlinetechvision.user;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * User Entity
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
@Entity
@Table(name="USER")
public class User {

    private int id;
    private String name;
    private String surname;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="ID", unique = true, nullable = false)
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name="NAME", unique = true, nullable = false)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Column(name="SURNAME", unique = true, nullable = false)
    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }  

    @Override
    public String toString() {
        StringBuffer strBuff = new StringBuffer();
        strBuff.append("id : ").append(getId());
        strBuff.append(", name : ").append(getName());
        strBuff.append(", surname : ").append(getSurname());
        return strBuff.toString();
    }
}

Шаг 5. Создание интерфейса IUserDAO

Интерфейс IUserDAO создан для предоставления доступа к данным.

package com.onlinetechvision.user.dao;

import java.util.List;

import com.onlinetechvision.user.User;

/**
 * User DAO Interface
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public interface IUserDAO {

    /**
     * Adds User
     *
     * @param  User user
     */
    void addUser(User user);

    /**
     * Gets User List
     *
     */
    List<User> getUsers();
}

Шаг 6: Создайте UserDAO IMPL

Класс UserDAO создается путем реализации интерфейса IUserDAO .

package com.onlinetechvision.user.dao;

import java.util.List;

import org.hibernate.SessionFactory;

import com.onlinetechvision.user.User;

/**
 * User DAO
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class UserDAO implements IUserDAO {

    private SessionFactory sessionFactory;

    /**
     * Gets Hibernate Session Factory
     *
     * @return SessionFactory - Hibernate Session Factory
     */
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * Sets Hibernate Session Factory
     *
     * @param SessionFactory - Hibernate Session Factory
     */
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * Adds User
     *
     * @param  User user
     */
    @Override
    public void addUser(User user) {
        getSessionFactory().getCurrentSession().save(user);
    }

    /**
     * Gets User List
     *
     * @return List - User list
     */
    @SuppressWarnings({ "unchecked" })
    @Override
    public List<User> getUsers() {
        List<User> list = getSessionFactory().getCurrentSession().createQuery("from User").list();
        return list;
    }

}

Шаг 7. Создание интерфейса IUserService

Интерфейс IUserService создан для сервисного уровня.

package com.onlinetechvision.user.service;

import java.util.List;

import com.onlinetechvision.user.User;

/**
 *
 * User Service Interface
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public interface IUserService {

    /**
     * Adds User
     *
     * @param  User user
     */
    void addUser(User user);

    /**
     * Gets User List
     *
     * @return List - User list
     */
    List<User> getUsers();
}

Шаг 8: Создайте UserService IMPL

Класс UserService создается путем реализации интерфейса IUserService .

package com.onlinetechvision.user.service;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import com.onlinetechvision.user.User;
import com.onlinetechvision.user.dao.IUserDAO;

/**
 *
 * User Service
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
@Transactional(readOnly = true)
public class UserService implements IUserService {

    IUserDAO userDAO;

    /**
     * Adds User
     *
     * @param  User user
     */
    @Transactional(readOnly = false)
    @Override
    public void addUser(User user) {
        getUserDAO().addUser(user);
    }

    /**
     * Gets User List
     *
     */
    @Override
    public List<User> getUsers() {
        return getUserDAO().getUsers();
    }

    public IUserDAO getUserDAO() {
        return userDAO;
    }

    public void setUserDAO(IUserDAO userDAO) {
        this.userDAO = userDAO;
    }
}

Шаг 9: Создать TestReader IMPL

Класс TestReader создается путем реализации интерфейса ItemReader . Этот класс вызывается для того, чтобы читать предметы. Когда метод чтения возвращает ноль, операция чтения завершена. Следующие шаги подробно объясняют, как выполнить firstJob.

Значение интервала фиксации firstjob равно 2, и выполняются следующие шаги:

1) firstTestReader вызывается для чтения первого элемента (firstname_0, firstsurname_0)
2) firstTestReader вызывается снова для чтения второго элемента (firstname_1, firstsurname_1)
3) testProcessor вызывается для обработки первого элемента (FIRSTNAME_0, FIRSTSURNAME_0)
4) testProcessor вызывается для обработки второй элемент (FIRSTNAME_1, FIRSTSURNAME_1)
5) testWriter называется написать первый элемент (FIRSTNAME_0, FIRSTSURNAME_0) к базе данных
6) testWriter вызывается для записи второй элемент (FIRSTNAME_1, FIRSTSURNAME_1) к базе данных
7) первый и второй элементы совершены и сделка закрыто.
8) firstTestReader вызывается для чтения третьего элемента (firstname_2, firstsurname_2)
9) значение maxIndex для firstTestReader равно 3. Метод read возвращает ноль, и операция чтения элемента завершена.
10) testProcessor вызывается для обработки третьего элемента (FIRSTNAME_2, FIRSTSURNAME_2)
11) testWriter вызывается для записи первого элемента (FIRSTNAME_2, FIRSTSURNAME_2) в базу данных
12) третий элемент фиксируется и транзакция закрывается.

firstStep завершается со статусом COMPLETED, а secondStep запускается. secondJob и thirdJob выполняются одинаково.

package com.onlinetechvision.item;

import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;

import com.onlinetechvision.user.User;

/**
 * TestReader Class is created to read items which will be processed
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class TestReader implements ItemReader<User> {
    private int index;
    private int maxIndex;
    private String namePrefix;
    private String surnamePrefix;

    /**
     * Reads items one by one
     *
     * @return User
     *
     * @throws Exception
     * @throws UnexpectedInputException
     * @throws ParseException
     * @throws NonTransientResourceException
     *
     */
    @Override
    public User read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        User user = new User();
        user.setName(getNamePrefix() + "_" + index);
        user.setSurname(getSurnamePrefix() + "_" + index);

        if(index > getMaxIndex()) {
            return null;
        }

        incrementIndex();

        return user;
    }

    /**
     * Increments index which defines read-count
     *
     * @return int
     *
     */
    private int incrementIndex() {
        return index++;
    }

    public int getMaxIndex() {
        return maxIndex;
    }

    public void setMaxIndex(int maxIndex) {
        this.maxIndex = maxIndex;
    }

    public String getNamePrefix() {
        return namePrefix;
    }

    public void setNamePrefix(String namePrefix) {
        this.namePrefix = namePrefix;
    }

    public String getSurnamePrefix() {
        return surnamePrefix;
    }

    public void setSurnamePrefix(String surnamePrefix) {
        this.surnamePrefix = surnamePrefix;
    }

}

Шаг 10: Создать FailedCaseTestReader IMPL

Класс FailedCaseTestReader создается для того, чтобы имитировать состояние невыполненного задания. В этом примере приложения, когда третий задание обрабатывается на пятом шаге, вызывается фаза failCaseTestReader и генерируется исключение, поэтому его состояние будет НЕУДАЧНО.

package com.onlinetechvision.item;

import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;

import com.onlinetechvision.user.User;

/**
 * FailedCaseTestReader Class is created in order to simulate the failed job status.
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class FailedCaseTestReader implements ItemReader<User> {
    private int index;
    private int maxIndex;
    private String namePrefix;
    private String surnamePrefix;

    /**
     * Reads items one by one
     *
     * @return User
     *
     * @throws Exception
     * @throws UnexpectedInputException
     * @throws ParseException
     * @throws NonTransientResourceException
     *
     */
    @Override
    public User read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        User user = new User();
        user.setName(getNamePrefix() + "_" + index);
        user.setSurname(getSurnamePrefix() + "_" + index);

        if(index >= getMaxIndex()) {
            throw new Exception("Unexpected Error!");
        }

        incrementIndex();

        return user;
    }

    /**
     * Increments index which defines read-count
     *
     * @return int
     *
     */
    private int incrementIndex() {
        return index++;
    }

    public int getMaxIndex() {
        return maxIndex;
    }

    public void setMaxIndex(int maxIndex) {
        this.maxIndex = maxIndex;
    }

    public String getNamePrefix() {
        return namePrefix;
    }

    public void setNamePrefix(String namePrefix) {
        this.namePrefix = namePrefix;
    }

    public String getSurnamePrefix() {
        return surnamePrefix;
    }

    public void setSurnamePrefix(String surnamePrefix) {
        this.surnamePrefix = surnamePrefix;
    }

}

Шаг 11: Создать TestProcessor IMPL

Класс TestProcessor создается путем реализации интерфейса ItemProcessor . Этот класс вызывается для обработки предметов. Пользовательский элемент получен из TestReader, обработан и возвращен в TestWriter.

package com.onlinetechvision.item;

import java.util.Locale;

import org.springframework.batch.item.ItemProcessor;

import com.onlinetechvision.user.User;

/**
 * TestProcessor Class is created to process items.
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class TestProcessor implements ItemProcessor<User, User>  {

    /**
     * Processes items one by one
     *
     * @param User user
     * @return User
     * @throws Exception
     *
     */
    @Override
    public User process(User user) throws Exception {
        user.setName(user.getName().toUpperCase(Locale.ENGLISH));
        user.setSurname(user.getSurname().toUpperCase(Locale.ENGLISH));
        return user;
    }

}

Шаг 12. Создание TestWriter IMPL

Класс TestWriter создается путем реализации интерфейса ItemWriter . Этот класс вызывается для записи элементов в БД, память и т. Д.

package com.onlinetechvision.item;

import java.util.List;

import org.springframework.batch.item.ItemWriter;

import com.onlinetechvision.user.User;
import com.onlinetechvision.user.service.IUserService;

/**
 * TestWriter Class is created to write items to DB, memory etc...
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class TestWriter implements ItemWriter<User> {

    private IUserService userService;

    /**
     * Writes items via list
     *
     * @throws Exception
     *
     */
    @Override
    public void write(List<? extends User> userList) throws Exception {
        for(User user : userList) {
            getUserService().addUser(user);
        }
        System.out.println("User List : " + getUserService().getUsers());
    }

    public IUserService getUserService() {
        return userService;
    }

    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

}

Шаг 13: создайте класс FailedStepTasklet

FailedStepTasklet создается путем реализации интерфейса Tasklet. Это иллюстрирует бизнес-логику в неудачном шаге.

package com.onlinetechvision.tasklet;

import org.apache.log4j.Logger;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

/**
 * FailedStepTasklet Class illustrates a failed job.
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class FailedStepTasklet implements Tasklet {

    private static final Logger logger = Logger.getLogger(FailedStepTasklet.class);

    private String taskResult;

    /**
     * Executes FailedStepTasklet
     *
     * @param StepContribution stepContribution
     * @param ChunkContext chunkContext
     * @return RepeatStatus
     * @throws Exception
     *
     */
    public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
        logger.debug("Task Result : " + getTaskResult());
        throw new Exception("Error occurred!");
    }

    public String getTaskResult() {
        return taskResult;
    }

    public void setTaskResult(String taskResult) {
        this.taskResult = taskResult;
    }

}

Шаг 14: Создайте класс BatchProcessStarter

Класс BatchProcessStarter создан для запуска заданий. Кроме того, это регистрирует их результаты выполнения.

package com.onlinetechvision.spring.batch;

import org.apache.log4j.Logger;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
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.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;

/**
 * BatchProcessStarter Class launches the jobs and logs their execution results.
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class BatchProcessStarter {

    private static final Logger logger = Logger.getLogger(BatchProcessStarter.class);

    private Job firstJob;
    private Job secondJob;
    private Job thirdJob;
    private JobLauncher jobLauncher;
    private JobRepository jobRepository;

    /**
     * Starts the jobs and logs their execution results.
     *
     */
    public void start() {
        JobExecution jobExecution = null;
        JobParametersBuilder builder = new JobParametersBuilder();

        try {
            getJobLauncher().run(getFirstJob(), builder.toJobParameters());
            jobExecution = getJobRepository().getLastJobExecution(getFirstJob().getName(), builder.toJobParameters());
            logger.debug(jobExecution.toString());         

            getJobLauncher().run(getSecondJob(), builder.toJobParameters());
            jobExecution = getJobRepository().getLastJobExecution(getSecondJob().getName(), builder.toJobParameters());
            logger.debug(jobExecution.toString());

            getJobLauncher().run(getThirdJob(), builder.toJobParameters());
            jobExecution = getJobRepository().getLastJobExecution(getThirdJob().getName(), builder.toJobParameters());
            logger.debug(jobExecution.toString());

        } catch (JobExecutionAlreadyRunningException
                    | JobRestartException
                    | JobInstanceAlreadyCompleteException
                    | JobParametersInvalidException e) {
            logger.error(e);
        }

    }  

    public Job getFirstJob() {
        return firstJob;
    }

    public void setFirstJob(Job firstJob) {
        this.firstJob = firstJob;
    }

    public Job getSecondJob() {
        return secondJob;
    }

    public void setSecondJob(Job secondJob) {
        this.secondJob = secondJob;
    }  

    public Job getThirdJob() {
        return thirdJob;
    }

    public void setThirdJob(Job thirdJob) {
        this.thirdJob = thirdJob;
    }

    public JobLauncher getJobLauncher() {
        return jobLauncher;
    }

    public void setJobLauncher(JobLauncher jobLauncher) {
        this.jobLauncher = jobLauncher;
    }

    public JobRepository getJobRepository() {
        return jobRepository;
    }

    public void setJobRepository(JobRepository jobRepository) {
        this.jobRepository = jobRepository;
    }  

}

Шаг 15: Создайте dataContext.xml

jdbc.properties , создан. Он определяет информацию об источнике данных и читается через dataContext.xml

jdbc.db.driverClassName=com.mysql.jdbc.Driver
jdbc.db.url=jdbc:mysql://localhost:3306/onlinetechvision
jdbc.db.username=root
jdbc.db.password=root
jdbc.db.initialSize=10
jdbc.db.minIdle=3
jdbc.db.maxIdle=10
jdbc.db.maxActive=10
jdbc.db.testWhileIdle=true
jdbc.db.testOnBorrow=true
jdbc.db.testOnReturn=true
jdbc.db.initSQL=SELECT 1 FROM DUAL
jdbc.db.validationQuery=SELECT 1 FROM DUAL
jdbc.db.timeBetweenEvictionRunsMillis=30000

Шаг 16. Создайте dataContext.xml

Файл конфигурации Spring, dataContext.xml , создан. Он охватывает определения dataSource, sessionFactory и TransactionsManager.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:batch="http://www.springframework.org/schema/batch"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd

http://www.springframework.org/schema/batch

http://www.springframework.org/schema/batch/spring-batch-2.1.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- Enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- Data Source Declaration -->
    <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"
                p:driverClassName="${jdbc.db.driverClassName}"
                p:url="${jdbc.db.url}"
                p:username="${jdbc.db.username}"
                p:password="${jdbc.db.password}"
                p:initialSize="${jdbc.db.initialSize}"
                p:minIdle="${jdbc.db.minIdle}"
                p:maxIdle="${jdbc.db.maxIdle}"
                p:maxActive="${jdbc.db.maxActive}"
                p:testWhileIdle="${jdbc.db.testWhileIdle}"
                p:testOnBorrow="${jdbc.db.testOnBorrow}"
                p:testOnReturn="${jdbc.db.testOnReturn}"
                p:initSQL="${jdbc.db.initSQL}"
                p:validationQuery="${jdbc.db.validationQuery}"
                p:timeBetweenEvictionRunsMillis="${jdbc.db.timeBetweenEvictionRunsMillis}"/>

    <!-- Session Factory Declaration -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>com.onlinetechvision.user.User</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <!-- Transaction Manager Declaration -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
       <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

</beans>

Шаг 17: Создать jobContext.xml

Файл конфигурации Spring, jobContext.xml , создан. Он охватывает jobRepository, jobLauncher, средство чтения элементов, обработчик элементов, средство записи элементов, задачи и определения заданий.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:batch="http://www.springframework.org/schema/batch"
    xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/batch

http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">

    <import resource="dataContext.xml"/>

    <!-- jobRepository Declaration -->
    <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
        <property name="transactionManager" ref="transactionManager" />
    </bean>

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

    <!-- Reader Bean Declarations -->
    <bean id="firstTestReader" class="com.onlinetechvision.item.TestReader">
        <property name="maxIndex" value="2"/>
        <property name="namePrefix" value="firstname"/>
        <property name="surnamePrefix" value="firstsurname"/>
    </bean>

    <bean id="secondTestReader" class="com.onlinetechvision.item.TestReader">
        <property name="maxIndex" value="2"/>
        <property name="namePrefix" value="secondname"/>
        <property name="surnamePrefix" value="secondsurname"/>
    </bean>

    <bean id="thirdTestReader" class="com.onlinetechvision.item.TestReader">
        <property name="maxIndex" value="3"/>
        <property name="namePrefix" value="thirdname"/>
        <property name="surnamePrefix" value="thirdsurname"/>
    </bean>

    <bean id="fourthTestReader" class="com.onlinetechvision.item.TestReader">
        <property name="maxIndex" value="3"/>
        <property name="namePrefix" value="fourthname"/>
        <property name="surnamePrefix" value="fourthsurname"/>
    </bean>

    <bean id="fifthTestReader" class="com.onlinetechvision.item.TestReader">
        <property name="maxIndex" value="3"/>
        <property name="namePrefix" value="fifthname"/>
        <property name="surnamePrefix" value="fifthsurname"/>
    </bean>

    <bean id="failedCaseTestReader" class="com.onlinetechvision.item.FailedCaseTestReader">
        <property name="maxIndex" value="1"/>
        <property name="namePrefix" value="failedcasename"/>
        <property name="surnamePrefix" value="failedcasesurname"/>
    </bean>

    <!-- Processor Bean Declaration -->
    <bean id="testProcessor" class="com.onlinetechvision.item.TestProcessor" />

    <!-- Writer Bean Declaration -->
    <bean id="testWriter" class="com.onlinetechvision.item.TestWriter" >
        <property name="userService" ref="userService"/>
    </bean>

    <!-- Failed Step Tasklet Declaration -->
    <bean id="failedStepTasklet" class="com.onlinetechvision.tasklet.FailedStepTasklet">
        <property name="taskResult"  value="Error occurred!" />
    </bean>

    <!-- Batch Job Declarations -->
    <batch:job id="firstJob">
        <batch:step id="firstStep" next="secondStep">
            <batch:tasklet>
                <batch:chunk reader="firstTestReader" processor="testProcessor" writer="testWriter" commit-interval="2"/>
            </batch:tasklet>
        </batch:step>
        <batch:step id="secondStep">
            <batch:tasklet>
                <batch:chunk reader="secondTestReader" processor="testProcessor" writer="testWriter" commit-interval="2"/>
            </batch:tasklet>
        </batch:step>
    </batch:job>

    <batch:job id="secondJob">
        <batch:step id="thirdStep">
            <batch:tasklet>
                <batch:chunk reader="thirdTestReader" processor="testProcessor" writer="testWriter" commit-interval="2"/>
            </batch:tasklet>
            <batch:next on="*" to="fourthStep" />
            <batch:next on="FAILED" to="firstFailedStep" />
        </batch:step>
        <batch:step id="fourthStep">
            <batch:tasklet>
                <batch:chunk reader="fourthTestReader" processor="testProcessor" writer="testWriter" commit-interval="2"/>
            </batch:tasklet>
        </batch:step>
        <batch:step id="firstFailedStep">
            <batch:tasklet ref="failedStepTasklet" />
        </batch:step>
    </batch:job>

    <batch:job id="thirdJob">
        <batch:step id="fifthStep">
            <batch:tasklet>
                <batch:chunk reader="failedCaseTestReader" processor="testProcessor" writer="testWriter" commit-interval="2"/>
            </batch:tasklet>
            <batch:next on="*" to="sixthStep" />
            <batch:next on="FAILED" to="secondFailedStep" />
        </batch:step>
        <batch:step id="sixthStep">
            <batch:tasklet>
                <batch:chunk reader="fifthTestReader" processor="testProcessor" writer="testWriter" commit-interval="2"/>
            </batch:tasklet>
        </batch:step>
        <batch:step id="secondFailedStep">
            <batch:tasklet ref="failedStepTasklet" />
        </batch:step>
    </batch:job>

</beans>

Шаг 18: Создайте applicationContext.xml

Файл конфигурации Spring, applicationContext.xml , создан. Он охватывает определения бобов.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:batch="http://www.springframework.org/schema/batch"
    xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/batch

http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">

    <import resource="jobContext.xml"/>

    <!-- User DAO Declaration -->
    <bean id="userDAO" class="com.onlinetechvision.user.dao.UserDAO">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- User Service Declaration -->
    <bean id="userService" class="com.onlinetechvision.user.service.UserService">
        <property name="userDAO" ref="userDAO" />
    </bean>  

    <!-- BatchProcessStarter Declaration -->
    <bean id="batchProcessStarter" class="com.onlinetechvision.spring.batch.BatchProcessStarter">
        <property name="jobLauncher" ref="jobLauncher"/>
        <property name="jobRepository" ref="jobRepository"/>
        <property name="firstJob" ref="firstJob"/>
        <property name="secondJob" ref="secondJob"/>
        <property name="thirdJob" ref="thirdJob"/>
    </bean>

</beans>

Шаг 19: Создайте класс приложения

Класс приложения создан для запуска приложения.

package com.onlinetechvision.exe;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.onlinetechvision.spring.batch.BatchProcessStarter;

/**
 * Application Class starts the application.
 *
 * @author onlinetechvision.com
 * @since 10 Dec 2012
 * @version 1.0.0
 *
 */
public class Application {

    /**
     * Starts the application
     *
     * @param  String[] args
     *
     */
    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        BatchProcessStarter batchProcessStarter = (BatchProcessStarter)appContext.getBean("batchProcessStarter");
        batchProcessStarter.start();
    }

}

Шаг 20: Построить проект

After OTV_SpringBatch_Chunk_Oriented_Processing Project is built, OTV_SpringBatch_Chunk_Oriented_Processing-0.0.1-SNAPSHOT.jar will be created.

STEP 21 : RUN PROJECT

After created OTV_SpringBatch_Chunk_Oriented_Processing-0.0.1-SNAPSHOT.jar file is run, the following database and console output logs will be shown :

Database screenshot :

First Job’ s console output :

16.12.2012 19:30:41  INFO (SimpleJobLauncher.java:118) - Job: [FlowJob: [name=firstJob]] launched with the following parameters: [{}]

16.12.2012 19:30:41 DEBUG (AbstractJob.java:278) - Job execution starting: JobExecution: id=0, version=0, startTime=null, endTime=null, lastUpdated=Sun Dec 16 19:30:41 GMT 2012, status=STARTING, exitStatus=exitCode=UNKNOWN;exitDescription=, job=[JobInstance: id=0, version=0, JobParameters=[{}], Job=[firstJob]]

User List : [id : 181, name : FIRSTNAME_0, surname : FIRSTSURNAME_0, id : 182, name : FIRSTNAME_1, surname : FIRSTSURNAME_1, id : 183, name : FIRSTNAME_2, surname : FIRSTSURNAME_2, id : 184, name : SECONDNAME_0, surname : SECONDSURNAME_0, id : 185, name : SECONDNAME_1, surname : SECONDSURNAME_1, id : 186, name : SECONDNAME_2, surname : SECONDSURNAME_2]

16.12.2012 19:30:42 DEBUG (BatchProcessStarter.java:43) - JobExecution: id=0, version=2, startTime=Sun Dec 16 19:30:41 GMT 2012, endTime=Sun Dec 16 19:30:42 GMT 2012, lastUpdated=Sun Dec 16 19:30:42 GMT 2012, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=0, version=0, JobParameters=[{}], Job=[firstJob]]

Second Job’ s console output :

16.12.2012 19:30:42  INFO (SimpleJobLauncher.java:118) - Job: [FlowJob: [name=secondJob]] launched with the following parameters: [{}]

16.12.2012 19:30:42 DEBUG (AbstractJob.java:278) - Job execution starting: JobExecution: id=1, version=0, startTime=null, endTime=null, lastUpdated=Sun Dec 16 19:30:42 GMT 2012, status=STARTING, exitStatus=exitCode=UNKNOWN;exitDescription=, job=[JobInstance: id=1, version=0, JobParameters=[{}], Job=[secondJob]]

User List : [id : 181, name : FIRSTNAME_0, surname : FIRSTSURNAME_0, id : 182, name : FIRSTNAME_1, surname : FIRSTSURNAME_1, id : 183, name : FIRSTNAME_2, surname : FIRSTSURNAME_2, id : 184, name : SECONDNAME_0, surname : SECONDSURNAME_0, id : 185, name : SECONDNAME_1, surname : SECONDSURNAME_1, id : 186, name : SECONDNAME_2, surname : SECONDSURNAME_2, id : 187, name : THIRDNAME_0, surname : THIRDSURNAME_0, id : 188, name : THIRDNAME_1, surname : THIRDSURNAME_1, id : 189, name : THIRDNAME_2, surname : THIRDSURNAME_2, id : 190, name : THIRDNAME_3, surname : THIRDSURNAME_3, id : 191, name : FOURTHNAME_0, surname : FOURTHSURNAME_0, id : 192, name : FOURTHNAME_1, surname : FOURTHSURNAME_1, id : 193, name : FOURTHNAME_2, surname : FOURTHSURNAME_2, id : 194, name : FOURTHNAME_3, surname : FOURTHSURNAME_3]

16.12.2012 19:30:42 DEBUG (BatchProcessStarter.java:47) - JobExecution: id=1, version=2, startTime=Sun Dec 16 19:30:42 GMT 2012, endTime=Sun Dec 16 19:30:42 GMT 2012, lastUpdated=Sun Dec 16 19:30:42 GMT 2012, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=1, version=0, JobParameters=[{}], Job=[secondJob]]

Third Job’ s console output :

16.12.2012 19:30:42  INFO (SimpleJobLauncher.java:118) - Job: [FlowJob: [name=thirdJob]] launched with the following parameters: [{}]

16.12.2012 19:30:42 DEBUG (AbstractJob.java:278) - Job execution starting: JobExecution: id=2, version=0, startTime=null, endTime=null, lastUpdated=Sun Dec 16 19:30:42 GMT 2012, status=STARTING, exitStatus=exitCode=UNKNOWN;exitDescription=, job=[JobInstance: id=2, version=0, JobParameters=[{}], Job=[thirdJob]]

16.12.2012 19:30:42 DEBUG (TransactionTemplate.java:159) - Initiating transaction rollback on application exception
org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Unexpected Error!
...

16.12.2012 19:30:43 DEBUG (BatchProcessStarter.java:51) - JobExecution: id=2, version=2, startTime=Sun Dec 16 19:30:42 GMT 2012, endTime=Sun Dec 16 19:30:43 GMT 2012, lastUpdated=Sun Dec 16 19:30:43 GMT 2012, status=FAILED, exitStatus=exitCode=FAILED;exitDescription=, job=[JobInstance: id=2, version=0, JobParameters=[{}], Job=[thirdJob]]

Step 22 : Download

https://github.com/erenavsarogullari/OTV_SpringBatch_Chunk_Oriented_Processing

REFERENCES :

Chunk Oriented Processing in Spring Batch