Статьи

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

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

1
2
3
4
5
6
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.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<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

1
2
3
4
5
6
7
8
9
<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 ,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    <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: СОЗДАТЬ СУБЪЕКТЫ

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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 создан для предоставления доступа к данным.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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 создан для сервисного уровня.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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) первый и второй пункты совершены и сделка закрыта.
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 выполняются одинаково.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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 и генерируется исключение, поэтому его состояние будет СБОЙ.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 . Этот класс вызывается для записи элементов в БД, память и т. Д.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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 CLASS

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 создан для запуска заданий. Кроме того, это регистрирует их результаты выполнения.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version='1.0' encoding='UTF-8'?>
       xmlns:context='http://www.springframework.org/schema/context'
       xsi:schemaLocation='http://www.springframework.org/schema/beans
 
 
 
 
 
 
 
 
    <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, средство чтения элементов, обработчик элементов, средство записи элементов, задачи и определения заданий.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?xml version='1.0' encoding='UTF-8'?>
    xsi:schemaLocation='http://www.springframework.org/schema/beans
 
 
 
 
    <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 , создан. Он охватывает определения бобов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version='1.0' encoding='UTF-8'?>
    xsi:schemaLocation='http://www.springframework.org/schema/beans
 
 
 
 
    <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: СОЗДАЙТЕ КЛАСС ПРИЛОЖЕНИЙ

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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: СТРОИМ ПРОЕКТ

После создания проекта OTV_SpringBatch_Chunk_Oriented_Processing будет создан файл OTV_SpringBatch_Chunk_Oriented_Processing-0.0.1-SNAPSHOT.jar .

ШАГ 21: ЗАПУСК ПРОЕКТА

После запуска созданного файла OTV_SpringBatch_Chunk_Oriented_Processing-0.0.1-SNAPSHOT.jar будут показаны следующие журналы вывода базы данных и консоли:

Скриншот базы данных:

Вывод консоли первого задания:

1
2
3
4
5
6
7
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]]

Вывод консоли второго задания:

1
2
3
4
5
6
7
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]]

Вывод консоли третьего задания:

1
2
3
4
5
6
7
8
9
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]]

ШАГ 22: СКАЧАТЬ
https://github.com/erenavsarogullari/OTV_SpringBatch_Chunk_Oriented_Processing

Ресурсы:

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

Ссылка: « Ориентированная на порцию обработка в пакетном режиме» от нашего партнера JCG Эрен Авсарогуллари в блоге Online Technology Vision .