웹사이트 검색

스프링 배치 예제


스프링 배치 예제에 오신 것을 환영합니다. Spring Batch는 배치 작업을 실행하기 위한 스프링 프레임워크 모듈입니다. 스프링 배치를 사용하여 일련의 작업을 처리할 수 있습니다.

스프링 배치 예제

스프링 배치 예제 프로그램을 살펴보기 전에 스프링 배치 용어에 대해 알아봅시다.

  • 작업은 'n'개의 단계로 구성될 수 있습니다. 각 단계에는 Read-Process-Write 작업이 포함되거나 tasklet이라는 단일 작업이 있을 수 있습니다.
  • 읽기-프로세스-쓰기는 기본적으로 데이터베이스, CSV 등과 같은 소스에서 읽은 다음 데이터를 처리하고 데이터베이스, CSV, XML 등과 같은 소스에 씁니다.
  • Tasklet은 연결 정리, 처리 완료 후 리소스 확보와 같은 단일 작업 또는 작업을 수행하는 것을 의미합니다.
  • 읽기-프로세스-쓰기 및 tasklet을 함께 연결하여 작업을 실행할 수 있습니다.

스프링 배치 예제

스프링 배치 구현을 위한 작업 예를 살펴보겠습니다. 구현 목적으로 다음 시나리오를 고려할 것입니다. 데이터가 포함된 CSV 파일은 데이터와 함께 XML로 변환되어야 하며 태그는 열 이름을 따라 이름이 지정됩니다. 다음은 스프링 배치 예제에 사용되는 중요한 도구 및 라이브러리입니다.

  1. Apache Maven 3.5.0 - 프로젝트 빌드 및 종속성 관리용.
  2. Eclipse Oxygen 릴리스 4.7.0 - 스프링 배치 메이븐 애플리케이션을 만들기 위한 IDE.
  3. 자바 1.8
  4. 스프링 코어 4.3.12.RELEASE
  5. 봄 OXM 4.3.12.RELEASE
  6. 스프링 JDBC 4.3.12.RELEASE
  7. 스프링 배치 3.0.8.RELEASE
  8. MySQL Java Driver 5.1.25 - MySQL 설치에 따라 사용합니다. 이는 Spring Batch 메타데이터 테이블에 필요합니다.

Spring Batch 예제 디렉토리 구조

스프링 배치 Maven 종속성

아래는 스프링 배치 예제 프로젝트에 필요한 모든 종속성을 포함하는 pom.xml 파일의 내용입니다.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.journaldev.spring</groupId>
	<artifactId>SpringBatchExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>SpringBatchDemo</name>
	<url>https://maven.apache.org</url>

	<properties>
		<jdk.version>1.8</jdk.version>
		<spring.version>4.3.12.RELEASE</spring.version>
		<spring.batch.version>3.0.8.RELEASE</spring.batch.version>
		<mysql.driver.version>5.1.25</mysql.driver.version>
		<junit.version>4.11</junit.version>
	</properties>

	<dependencies>

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

		<!-- Spring jdbc, for database -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring XML to/back object -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- MySQL database driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.driver.version}</version>
		</dependency>

		<!-- Spring Batch dependencies -->
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-core</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-infrastructure</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>

		<!-- Spring Batch unit test -->
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-test</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>

		<!-- Junit -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.thoughtworks.xstream</groupId>
			<artifactId>xstream</artifactId>
			<version>1.4.10</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>spring-batch</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>2.9</version>
				<configuration>
					<downloadSources>true</downloadSources>
					<downloadJavadocs>false</downloadJavadocs>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>${jdk.version}</source>
					<target>${jdk.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

스프링 배치 처리 CSV 입력 파일

다음은 스프링 배치 처리를 위한 샘플 CSV 파일의 내용입니다.

1001,Tom,Moody, 29/7/2013
1002,John,Parker, 30/7/2013
1003,Henry,Williams, 31/7/2013

스프링 배치 작업 구성

구성 파일에서 스프링 빈과 스프링 배치 작업을 정의해야 합니다. 아래는 Spring Batch 프로젝트의 가장 중요한 부분인 job-batch-demo.xml 파일의 내용입니다.

<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:batch="https://www.springframework.org/schema/batch" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.springframework.org/schema/batch
		https://www.springframework.org/schema/batch/spring-batch-3.0.xsd
		https://www.springframework.org/schema/beans 
		https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
	">

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

	<bean id="report" class="com.journaldev.spring.model.Report"
		scope="prototype" />
	<bean id="itemProcessor" class="com.journaldev.spring.CustomItemProcessor" />

	<batch:job id="DemoJobXMLWriter">
		<batch:step id="step1">
			<batch:tasklet>
				<batch:chunk reader="csvFileItemReader" writer="xmlItemWriter"
					processor="itemProcessor" commit-interval="10">
				</batch:chunk>
			</batch:tasklet>
		</batch:step>
	</batch:job>

	<bean id="csvFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">

		<property name="resource" value="classpath:csv/input/report.csv" />

		<property name="lineMapper">
			<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
				<property name="lineTokenizer">
					<bean
						class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
						<property name="names" value="id,firstname,lastname,dob" />
					</bean>
				</property>
				<property name="fieldSetMapper">
					<bean class="com.journaldev.spring.ReportFieldSetMapper" />

					<!-- if no data type conversion, use BeanWrapperFieldSetMapper to map 
						by name <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"> 
						<property name="prototypeBeanName" value="report" /> </bean> -->
				</property>
			</bean>
		</property>

	</bean>

	<bean id="xmlItemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
		<property name="resource" value="file:xml/outputs/report.xml" />
		<property name="marshaller" ref="reportMarshaller" />
		<property name="rootTagName" value="report" />
	</bean>

	<bean id="reportMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
		<property name="classesToBeBound">
			<list>
				<value>com.journaldev.spring.model.Report</value>
			</list>
		</property>
	</bean>

</beans>

  1. FlatFileItemReader를 사용하여 CSV 파일을 읽고, CustomItemProcessor를 사용하여 데이터를 처리하고 StaxEventItemWriter를 사용하여 XML 파일에 씁니다.
  2. batch:job – 이 태그는 생성하려는 작업을 정의합니다. Id 속성은 작업의 ID를 지정합니다. 단일 xml 파일에 여러 작업을 정의할 수 있습니다.
  3. batch:step – 이 태그는 스프링 배치 작업의 여러 단계를 정의하는 데 사용됩니다.
  4. Spring Batch Framework에서는 "TaskletStep Oriented\ 및 "Chunk Oriented\라는 두 가지 유형의 처리 스타일을 제공합니다. 이 예제에서 사용되는 Chunk Oriented 스타일은 트랜잭션 경계 내에서 데이터를 하나씩 읽고 쓸 '청크'를 생성하는 것을 의미합니다.
  5. reader: 데이터 읽기에 사용되는 스프링 빈. 이 예제에서는 FlatFileItemReader의 인스턴스인 csvFileItemReader 빈을 사용했습니다.
  6. 프로세서: 데이터 처리에 사용되는 클래스입니다. 이 예제에서는 CustomItemProcessor를 사용했습니다.
  7. writer: xml 파일에 데이터를 쓰는 데 사용되는 bean.
  8. commit-interval: 이 속성은 처리가 완료되면 커밋될 청크의 크기를 정의합니다. 기본적으로 ItemReader가 데이터를 하나씩 읽고 ItemProcessor도 같은 방식으로 처리하지만 ItemWriter는 커밋 간격의 크기와 같을 때만 데이터를 씁니다.
  9. 이 프로젝트의 일부로 사용되는 세 가지 중요한 인터페이스는 org.springframework.batch의 ItemReader, ItemProcessorItemWriter입니다. .item 패키지.

스프링 배치 모델 클래스

우선 CSV 파일을 Java 객체로 읽은 다음 주석을 사용합니다.

package com.journaldev.spring.model;

import java.util.Date;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "record")
public class Report {

	private int id;
	private String firstName;
	private String lastName;
	private Date dob;

	@XmlAttribute(name = "id")
	public int getId() {
		return id;
	}

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

	@XmlElement(name = "firstname")
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	@XmlElement(name = "lastname")
	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@XmlElement(name = "dob")
	public Date getDob() {
		return dob;
	}

	public void setDob(Date dob) {
		this.dob = dob;
	}

	@Override
	public String toString() {
		return "Report [id=" + id + ", firstname=" + firstName + ", lastName=" + lastName + ", DateOfBirth=" + dob
				+ "]";
	}

}

모델 클래스 필드는 스프링 배치 매퍼 구성에 정의된 것과 동일해야 합니다. 즉, 우리의 경우 property name=\names\ value=\id,firstname,lastname,dob\입니다.

스프링 배치 FieldSetMapper

날짜를 변환하려면 사용자 정의 FieldSetMapper가 필요합니다. 데이터 유형 변환이 필요하지 않은 경우 BeanWrapperFieldSetMapper만 이름별로 값을 자동으로 매핑하는 데 사용해야 합니다. FieldSetMapper를 확장하는 Java 클래스는 ReportFieldSetMapper입니다.

package com.journaldev.spring;

import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;

import com.journaldev.spring.model.Report;

public class ReportFieldSetMapper implements FieldSetMapper<Report> {

	private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

	public Report mapFieldSet(FieldSet fieldSet) throws BindException {

		Report report = new Report();
		report.setId(fieldSet.readInt(0));
		report.setFirstName(fieldSet.readString(1));
		report.setLastName(fieldSet.readString(2));

		// default format yyyy-MM-dd
		// fieldSet.readDate(4);
		String date = fieldSet.readString(3);
		try {
			report.setDob(dateFormat.parse(date));
		} catch (ParseException e) {
			e.printStackTrace();
		}

		return report;

	}

}

스프링 배치 아이템 프로세서

이제 작업 구성에 정의된 대로 itemWriter보다 itemProcessor가 실행됩니다. 동일한 CustomItemProcessor.java 클래스를 만들었습니다.

package com.journaldev.spring;

import org.springframework.batch.item.ItemProcessor;

import com.journaldev.spring.model.Report;

public class CustomItemProcessor implements ItemProcessor<Report, Report> {

	public Report process(Report item) throws Exception {
		
		System.out.println("Processing..." + item);
		String fname = item.getFirstName();
		String lname = item.getLastName();
		
		item.setFirstName(fname.toUpperCase());
		item.setLastName(lname.toUpperCase());
		return item;
	}

}

이름과 성 값을 대문자로 변환하는 것을 볼 수 있으므로 ItemProcessor 구현에서 데이터를 조작할 수 있습니다.

스프링 구성 파일

스프링 배치 구성 파일에서 두 개의 추가 구성 파일인 context.xmldatabase.xml을 가져왔습니다.

<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		https://www.springframework.org/schema/beans 
		https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

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

</beans>

  • jobRepository - JobRepository는 스프링 배치를 위한 올바른 메타 데이터 테이블에 각 Java 개체를 저장하는 역할을 합니다.
  • transactionManager- 커밋 간격의 크기와 처리된 데이터가 동일하면 트랜잭션 커밋을 담당합니다.
  • jobLauncher – 스프링 배치의 핵심입니다. 이 인터페이스에는 작업을 트리거하는 데 사용되는 실행 메서드가 포함되어 있습니다.

<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:jdbc="https://www.springframework.org/schema/jdbc" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.springframework.org/schema/beans 
		https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		https://www.springframework.org/schema/jdbc 
		https://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd">

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

	<bean id="transactionManager"
		class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

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

Spring Batch는 일부 메타데이터 테이블을 사용하여 배치 작업 정보를 저장합니다. 스프링 배치 구성에서 생성할 수 있지만 위의 주석 코드에서 볼 수 있듯이 SQL 파일을 실행하여 수동으로 수행하는 것이 좋습니다. 보안 관점에서 스프링 배치 데이터베이스 사용자에게 DDL 실행 액세스 권한을 부여하지 않는 것이 좋습니다.

스프링 배치 테이블

  1. Batch_job_instance: BATCH_JOB_INSTANCE 테이블은 JobInstance와 관련된 모든 정보를 보유합니다.
  2. Batch_job_execution_params: BATCH_JOB_EXECUTION_PARAMS 테이블에는 JobParameters 개체와 관련된 모든 정보가 들어 있습니다.
  3. Batch_job_execution: BATCH_JOB_EXECUTION 테이블은 JobExecution 객체와 관련된 데이터를 보유합니다. 작업이 실행될 때마다 새 행이 추가됩니다.
  4. Batch_step_execution: BATCH_STEP_EXECUTION 테이블에는 StepExecution 개체와 관련된 모든 정보가 들어 있습니다.
  5. Batch_job_execution_context: BATCH_JOB_EXECUTION_CONTEXT 테이블은 Job의 ExecutionContext와 관련된 데이터를 보유합니다. 모든 JobExecution에 대해 정확히 하나의 Job ExecutionContext가 있으며 특정 작업 실행에 필요한 모든 작업 수준 데이터를 포함합니다. 이 데이터는 일반적으로 JobInstance가 실패한 위치에서 다시 시작할 수 있도록 실패 후 검색해야 하는 상태를 나타냅니다.
  6. Batch_step_execution_context: BATCH_STEP_EXECUTION_CONTEXT 테이블은 단계의 ExecutionContext와 관련된 데이터를 보유합니다. 모든 StepExecution에 대해 정확히 하나의 ExecutionContext가 있으며 특정 단계 실행을 위해 지속되어야 하는 모든 데이터를 포함합니다. 이 데이터는 일반적으로 JobInstance가 실패한 위치에서 다시 시작할 수 있도록 실패 후 검색해야 하는 상태를 나타냅니다.
  7. Batch_job_execution_seq: 이 테이블은 작업의 데이터 실행 순서를 보유합니다.
  8. Batch_step_execution_seq: 이 테이블은 단계 실행을 위한 시퀀스 데이터를 보유합니다.
  9. Batch_job_seq: 이 테이블은 작업이 여러 개인 경우 여러 행을 얻을 수 있도록 작업 순서에 대한 데이터를 보유합니다.

스프링 배치 테스트 프로그램

Spring Batch 예제 프로젝트가 준비되었습니다. 마지막 단계는 테스트 클래스를 작성하여 Java 프로그램으로 실행하는 것입니다.

package com.journaldev.spring;

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

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

		String[] springConfig = { "spring/batch/jobs/job-batch-demo.xml" };

		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(springConfig);

		JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
		Job job = (Job) context.getBean("DemoJobXMLWriter");

		JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
				.toJobParameters();

		try {

			JobExecution execution = jobLauncher.run(job, jobParameters);
			System.out.println("Exit Status : " + execution.getStatus());

		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("Done");
		context.close();
	}
}

위의 프로그램을 실행하면 아래와 같은 출력 xml을 얻을 수 있습니다.

<?xml version="1.0" encoding="UTF-8"?><report><record id="1001"><dob>2013-07-29T00:00:00+05:30</dob><firstname>TOM</firstname><lastname>MOODY</lastname></record><record id="1002"><dob>2013-07-30T00:00:00+05:30</dob><firstname>JOHN</firstname><lastname>PARKER</lastname></record><record id="1003"><dob>2013-07-31T00:00:00+05:30</dob><firstname>HENRY</firstname><lastname>WILLIAMS</lastname></record></report>

Spring Batch 예제는 여기까지입니다. 아래 링크에서 최종 프로젝트를 다운로드할 수 있습니다.

스프링 배치 예제 프로젝트 다운로드

참조: 공식 가이드