-
Notifications
You must be signed in to change notification settings - Fork 0
Manual Spring Batch
O objetivo deste manual é descrever os passos para a criação de Jobs com Spring Batch. As ferramentas utilizadas serão:
- VSCode
- JDK
- Maven
- Spring
- PostgreSQL
-
Instale o Oracle VirtualBox
-
Faça o download do Lubuntu
-
Crie uma nova máquina virtual e siga os passos para instalação do Lubuntu
-
No terminal, execute os seguintes comandos:
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install gcc
$ sudo apt-get install make
-
Na máquina virtual, acesse o menu Dispositivos -> Inserir imagem do CD dos adicionais para convidado...
-
No terminal, navegue até o diretório montado e digite
$ sudo ./VBoxLinuxAdditions.run
- Reinicie a VM
- Crie um diretório e navegue para dentro dele
$ mkdir Tools
$ cd Tools
- Dentro do diretório, crie o script configure.sh com o seguinte conteúdo
# Instalação e configuração do JDK
sudo apt-get install -y python-software-properties
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install -y oracle-java8-installer
# Instalação do Maven
apt-cache search maven
sudo apt-get install maven
# Instalação e configuração do PostgreSQL
sudo apt-get install -q -y postgresql pgadmin3
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD 'postgres';"
POSTGRESQL_DIR="$(find /etc/postgresql -type d | grep main)"
POSTGRESQL_CONF=$POSTGRESQL_DIR"/postgresql.conf"
sudo chmod 777 $POSTGRESQL_CONF
sudo sed -i "59s/.*/listen_addresses = '*'/" $POSTGRESQL_CONF
PG_HBA_CONF=$POSTGRESQL_DIR"/pg_hba.conf"
sudo chmod 777 $PG_HBA_CONF
sudo sed -i "85s/.*/local all postgres trust/" $PG_HBA_CONF
sudo echo "host all all all password" >> $PG_HBA_CONF
sudo /etc/init.d/postgresql restart
# Instalação do editor VSCode
wget -O VSCode.tar.gz https://go.microsoft.com/fwlink/?LinkID=620884;
tar -xvzf VSCode.tar.gz;
rm VSCode.tar.gz;
sudo ln -s $(pwd)/VSCode-linux-x64/code /usr/local/bin/code;
sudo apt-get install libgconf-2-4
cd ..
sudo chmod -R 777 Tools
- Execute o script
$ sudo sh configure.sh | tee -a logfile
- Dentro da pasta do projeto, crie a seguinte estrutura de diretório:
└── src
└── main
└── java
└── hello
$ mkdir -p src/main/java/hello
- Na raiz do projeto, crie o arquivo pom.xml com o seguinte conteúdo:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-batch-processing</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
O arquivo pom.xml contém as dependências do projeto que serão baixadas e configuradas pelo Maven.
- Configuração da persistência de dados
Inicialmente, a persistência de dados será realizada utilizando o HSQLDB (HyperSQL DataBase). Por padrão, o Spring utiliza o HSQLDB armazenando os dados em memória.
- Crie o arquivo src/main/resources/sample-data.csv com o seguinte conteúdo:
Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe
Este arquivo contém os registros de nome e sobrenome que serão carregados em uma tabela de banco de dados de pessoas.
- Crie o arquivo src/main/resources/schema-all.sql com o seguinte conteúdo:
DROP TABLE people IF EXISTS;
CREATE TABLE people (
person_id BIGINT IDENTITY NOT NULL PRIMARY KEY,
first_name VARCHAR(20),
last_name VARCHAR(20)
);
Os dados do arquivo anterior serão carregados nessa tabela.
Em sua configuração padrão, o Spring executa schema-@@platforma@@.sql para gerar o esquema. Ao utilizar -all, informamos ao Spring que o esquema poderá ser gerado em qualquer plataforma. Neste momento, será utilizado o esquema do HSQLDB.
- Criação das classes do projeto
- Crie a classe src/main/java/hello/Person.java para encapsular os dados de pessoas vindos do banco.
package hello;
public class Person {
private String lastName;
private String firstName;
public Person() {
}
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "firstName: " + firstName + ", lastName: " + lastName;
}
}
- Crie um ItemProcessor para o job em src/main/java/hello/PersonItemProcessor.java com o seguinte conteúdo:
package hello;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
public class PersonItemProcessor implements ItemProcessor<Person, Person> {
private static final Logger log = LoggerFactory.getLogger(PersonItemProcessor.class);
@Override
public Person process(final Person person) throws Exception {
final String firstName = person.getFirstName().toUpperCase();
final String lastName = person.getLastName().toUpperCase();
final Person transformedPerson = new Person(firstName, lastName);
log.info("Converting (" + person + ") into (" + transformedPerson + ")");
return transformedPerson;
}
}
O Processor é um componente que realiza a lógica de negócio para um Step. Por sua vez, o Step é um objeto que representa um passo individual de um Job. Em Xml, a estrutura ficaria da seguinte forma:
<job id="peopleJob">
<step id="step1">
<tasklet>
<chunk processor="personItemProcessor"... />
</tasklet>
</step>
</job>
A figura a seguir ilustra a os relacionamentos entre Job, Step e Processor:
A lógica de negócio da classe PersonItemProcessor consiste em transformar o objeto em outro com os dados em caixa alta.
- Crie uma classe para configurar a execução do Job em src/main/java/hello/BatchConfiguration.java:
package hello;
import javax.sql.DataSource;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
// tag::readerwriterprocessor[]
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names(new String[]{"firstName", "lastName"})
.fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<Person>()
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
.dataSource(dataSource)
.build();
}
// end::readerwriterprocessor[]
// tag::jobstep[]
@Bean
public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
return jobBuilderFactory.get("importUserJob")
.incrementer(new RunIdIncrementer())
.listener(listener)
.flow(step1)
.end()
.build();
}
@Bean
public Step step1(JdbcBatchItemWriter<Person> writer) {
return stepBuilderFactory.get("step1")
.<Person, Person> chunk(10)
.reader(reader())
.processor(processor())
.writer(writer)
.build();
}
// end::jobstep[]
}
Vamos começar analisando as anotações utilizadas:
-
@Configuration indica que a classe contém definições de beans, ou seja, métodos anotados com @Bean. Uma classe anotada com @Configuration é implicitamente anotada com um @Component. A diferença que existe quando se utiliza @Configuration é que um método anotado com @Bean pode ser chamado por outro método anotado com @Bean.
-
@EnableBatchProcessing habilita features do Spring Batch, tais como o banco de dados baseado em memória (HSQLDB) utilizado nesse exemplo, além de configurações básicas para a criação do Job. Isso permite que o programador se preocupe apenas com as regras de negócio.
-
@Bean cria um bean de configuração gerenciado pelo container.
-
@Autowired permite que o Spring injete dependências na classe. Neste exemplo, jobBuilderFactory e stepBuilderFactory serão instanciados pelo container.
Métodos:
- reader() lê dos dados do arquivo sample-data.csv para o processamento do Job.
- processor() realiza o processamento das regras do Job. Aqui é executado o método process() de PersonItemProcessor criado no passo anterior.
- writer(DataSource dataSource) grava os dados processados em uma tabela por meio de um DataSource injetado pela anotação @EnableBatchProcessing.
- importUserJob(JobCompletionNotificationListener listener, Step step1) define o Job, indicando quais Steps serão executados. Neste exemplo, temos apenas um Step, definido no método seguinte, mas poderíamos ter vários. Nesta definição é necessário um incrementer, pois os jobs utilizam uma base de dados para manter o estado de execução.
- step1(JdbcBatchItemWriter writer) define um Step, que contém um reader, um processor e um writer. A chamada chunk(10) indica quantos dados serão escritos por vez.