+2

Spring Batch Hello World

Trong quá trình làm việc và học tập chúng ta có thể gặp một số khái niệm mới.Bài viết này tôi xin giới thiệu về khái niệm mới tôi đã gặp trong quá trình học tập làm việc đó là Spring batch.

1.Khái niệm

1.1 Batch là gì?

Batch hoặc Batch processing là từ được sử dụng trong ngành công nghệ thông tin để tả cách xử lý hàng loạt của những chương trình vi tính mà không cần sự can thiệp của nhân viên.

Ví dụ: Mỗi cuối tháng, các chương trình tính lương tính và in hàng nghìn bản lương để trả cho nhân viên. Các chương trình này thường chạy theo cách batch:

Đọc tập tin "hồ sơ nhân viên" của hãng
Tính lương tháng cho nhân viên
In bản lương tháng cho nhân viên
Đọc và xử lý hồ sơ tiếp theo...

Khi chương trình ngưng chạy thì nó đã tự động xử lý hàng trăm, hàng ngàn bản lương, không cần người nào làm gì thêm trong lúc chương trình này đang chạy.

1.2 Spring Batch là gì?

Spring Batch is một framework cho việc xử lý batch-một chuỗi các công việc,trong spring batch,một công việc bao gồm nhiều steps và mỗi step bao gồm một nhiệm vụ là READ-PROCESS-WRITE hoặc một task chỉ gồm một hành động(tasklet)

Đối với việc “READ-PROCESS-WRITE”,nó có nghĩa là đọc dữ liệu từ một nguồn nào đó(csv,xml hoặc database),xử lý và ghi vào một nguồn khác như(csv,xml và database).Ví dụ một bước đọc dữ liệu từ một file csv,xử lý dữ liệu và ghi nó vào database,Spring Batch cung cấp nhiều các class để đọc và ghi csv,xml và database. Đối với nhiệm vụ mà có một hành động như là dọn dẹp các nguyên trước hoặc sau khi một step bắt đầu hoặc kết thúc

Các step có thể được xâu chuỗi cùng nhau để chạy như một job

1 Job = Mnay steps

1 step = 1 READ-PROCESS-WRITE or 1 Tasklet.

Job = {Step 1 -> Step 2 -> Step 3} (Chained together)

Spring Batch cung cấp một tập hợp các reusable function (các hàm có thể tái sử dụng). Các hàm này giữ vai trò trọng yếu khi xử lý một lượng lớn các bản ghi trong CSDL.Bao gồm logging/tracing,quản lý transaction ,thống kê xử lý job,khởi động lại job,skip và quản lý tài nguyên.Nó cũng cung cấp nhiều các dịch vụ tiện ích và các tính năng có thể ở quy mô lớn,và nâng cao hiệu năng của các batch job thông qua việc tối ưu hóa và công nghệ partitioning .Đơn gian cũng như phức tạp,các công việc xử lý batch lớn có thể tận dụng framework này trong việc quản lý khả năng mở rộng để xử lý khối lượng lớn thông tin

2.ví dụ nhỏ về implement spring batch sử dụng maven Bài toán:ta có một file chứa các bản ghi doanh thu của từng nhân viên.và ta tiến hành xử lý các bản ghi sau đó ghi vào một file csv khác. Sau khi tạo project với maven.ta bắt đầu với việc cập nhật các thư viên cần thiết cho spring batch trong file pom.xml như sau pom.xml

<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>spring</groupId>
<artifactId>Final-Spring-Hibernate</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
	<java.version>1.8</java.version>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	<log4j.version>1.2.17</log4j.version>
	<jcl.slf4j.version>1.7.12</jcl.slf4j.version>
	<logback.version>1.1.3</logback.version>
	<springframework.version>4.2.5.RELEASE</springframework.version>
	<spring-security.version>4.0.4.RELEASE</spring-security.version>
	<spring-social.version>1.1.4.RELEASE</spring-social.version>
	<org.hibernate.version>4.3.5.Final</org.hibernate.version>
	<spring-orm.version>4.3.2.RELEASE</spring-orm.version>
	<hibernate-validator.version>5.2.4.Final</hibernate-validator.version>
	<junit.version>4.12</junit.version>
	<quartz.version>2.2.1</quartz.version>
	<spring.batch.version>3.0.3.RELEASE</spring.batch.version>
	<mysql.driver.version>5.1.25</mysql.driver.version>
	<spring-jdbc.version>3.2.2.RELEASE</spring-jdbc.version>

</properties>

<repositories>
	<repository>
		<id>codelds</id>
		<url>https://code.lds.org/nexus/content/groups/main-repo</url>
	</repository>
	<repository>
		<id>osssonatype</id>
		<name>OSS Sonatype</name>
		<url>https://repo1.maven.org/maven2/</url>
		<layout>default</layout>
	</repository>
</repositories>

<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>

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

</dependencies>
<build>
	<finalName>SpringMVCSocialJdbc</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>${java.version}</source>
				<target>${java.version}</target>
			</configuration>
		</plugin>
	</plugins>
</build></project>

Trong ví dụ ta giả sử dữ liệu đầu vào là một file csv(comma seperate value) file report.csv

    1001,"213,100",980,"mykyong", 29/7/2013
    1002,"320,200",1080,"staff 1", 30/7/2013
    1003,"342,197",1200,"staff 2", 31/7/2013

Trong ví dụ ta sẽ đọc file csv bằng đối tượng FlatFileItemReader, xử lý dữ liệu với với đối tượng được implement interface itemProcessor và ghi kết quả vào XML file với StaxEventItemWriter.

   <?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<import resource="classpath*:/spring/batch/config/database.xml" />
<import resource="classpath*:/spring/batch/config/context.xml" />

<bean id="report" class="study.batch.Report" scope="prototype">
</bean>
<bean id="customItemReportProcessor" class="study.batch.CustomItemReportProcessor">
</bean>
<bean id="fieldSetMapper" class="study.batch.FieldSetReportMapper">
</bean>
<batch:job id="wellcome">
	<batch:step id="step1">
		<batch:tasklet>
			<batch:chunk writer="staxEventItemWriter" processor="customItemReportProcessor"
				reader="flatFileItemReader" commit-interval="10">
			</batch:chunk>
		</batch:tasklet>
	</batch:step>
</batch:job>

<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
	<property name="resource"
		value="classpath:spring/batch/csv/input/report.csv"></property>
	<property name="lineMapper">
		<bean id="defaultLineMapper"
			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,sales,qty,staffName,date"></property>
				</bean>
			</property>
			<property name="fieldSetMapper" ref="fieldSetMapper"></property>
		</bean>
	</property>

</bean>

<bean id="staxEventItemWriter" 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">//lớp này dùng để convert đối dượng java sử dụng anoation thành các dòng trong file
	<property name="classesToBeBound">
		<list>
			<value>study.batch.Report</value>
		</list>
	</property>
</bean>
</beans>

Sau model hóa bản ghi ta được đối tượng report.Chú ý ta có thêm các annotation xml binding dùng để chuyển đổi đối tượng java thành các cấu trúc trong xml và ngược lại model report.java

package study.batch;
import java.math.BigDecimal;
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 BigDecimal sales;
private int qty;
private String staffName;
private Date date;

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

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

@XmlElement(name = "sales")
public BigDecimal getSales() {
    return sales;
}

public void setSales(BigDecimal sales) {
    this.sales = sales;
}

@XmlElement(name = "qty")
public int getQty() {
    return qty;
}

public void setQty(int qty) {
    this.qty = qty;
}

@XmlElement(name = "staffName")
public String getStaffName() {
    return staffName;
}

public void setStaffName(String staffName) {
    this.staffName = staffName;
}

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

@Override
public String toString() {
    return "Report [id=" + id + ", sales=" + sales + ", qty=" + qty + ", staffName=" + staffName + "]";
}

}

FieldSetReportMapper:Để convert date,thì ta cần custom FieldSetMapper,Nếu không có loại dữ liệu cần convert thì chỉ cần BeanWrapperFieldSetMapper để mapping dữ liệu tự động theo tên

package study.batch;
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 study.batch.Report;
public class ReportFieldSetMapper implements FieldSetMapper<Report> {

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

@Override
public Report mapFieldSet(FieldSet fieldSet) throws BindException {

	Report report = new Report();
	report.setId(fieldSet.readInt(0));
	report.setSales(fieldSet.readBigDecimal(1));
	report.setQty(fieldSet.readInt(2));
	report.setStaffName(fieldSet.readString(3));

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

	return report;

}}

CustomItemProcessor.java :là một implement của itemProcessor có nhiệm vụ xứ lý dữ liệu sau khi đọc và trả về dữ liệu kết quả.Tại đây bạn sẽ viết các bussiness đối với các bản ghi.

package study.batch;
import org.springframework.batch.item.ItemProcessor;
import study.batch.Report;
public class CustomItemProcessor implements ItemProcessor<Report, Report> {

@Override
public Report process(Report item) throws Exception {

	System.out.println("Processing..." + item);
	return item;
}

}

Các file cấu hình:Spring context và database.

context.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:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

        <bean id="jobRepository"
	class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
	<property name="dataSource" ref="dataSource"></property>
	<property name="databaseType" value="mysql"></property>
	<property name="transactionManager" ref="transactionManager"></property>
</bean>
<bean id="transactionManager"
	class="org.springframework.batch.support.transaction.ResourcelessTransactionManager">
</bean>
<bean id="simpleJobLauncher"
	class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
	<property name="jobRepository" ref="jobRepository"></property>
    </bean></beans>

database.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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:property-placeholder
        location="classpath*:/database/database.properties" />
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${ds.driver.manager}"></property>
        <property name="url" value="${ds.url}"></property>
        <property name="username" value="${ds.username}"></property>
        <property name="password" value="${ds.password}"></property>
    </bean>
</beans>

và cuối cùng là file chạy chương trình Apptest.java

package study.batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App_Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                new String[] { "spring/batch/jobs/wellcome-job.xml" });
        JobLauncher jobLauncher = (SimpleJobLauncher) applicationContext.getBean("simpleJobLauncher");
        Job job = (Job) applicationContext.getBean("wellcome");
        try {
            JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
            System.out.println("chay xong");
        } catch (JobExecutionAlreadyRunningException e) {
            e.printStackTrace();
        } catch (JobRestartException e) {
            e.printStackTrace();
        } catch (JobInstanceAlreadyCompleteException e) {
            e.printStackTrace();
        } catch (JobParametersInvalidException e) {
            e.printStackTrace();
        }

    }

}

Hi vọng thông qua ví dụ nhỏ này sẽ giúp bạn trong công việc cũng như học tập,chúc may mắn tài liệu tham khảo

https://vi.wikipedia.org/wiki/Batch
http://docs.spring.io/spring-batch/reference/html/spring-batch-intro.html
http://www.mkyong.com/spring-batch/spring-batch-hello-world-example/

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.