Обработка больших наборов данных — одна из важнейших проблем в мире программного обеспечения. 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