مثال على دفعة الربيع

مرحبًا بك في مثال Spring Batch. يعد Spring Batch وحدة في إطار Spring لتنفيذ وظائف الدفعة. يمكننا استخدام Spring Batch لمعالجة سلسلة من الوظائف.

مثال Spring Batch

قبل أن نتعرف على برنامج مثال Spring Batch ، دعنا نحصل على بعض الأفكار حول مصطلحات Spring Batch.

  • A job can consist of ‘n’ number of steps. Each step contains Read-Process-Write task or it can have single operation, which is called tasklet.
  • القراءة-المعالجة-الكتابة هي في الأساس القراءة من مصدر مثل قاعدة البيانات ، CSV إلخ ، ثم معالجة البيانات وكتابتها في مصدر مثل قاعدة البيانات ، CSV ، XML إلخ.
  • المهمة تعني القيام بمهمة أو عملية واحدة مثل تنظيف الاتصالات ، وتحرير الموارد بعد الانتهاء من المعالجة.
  • يمكن ربط قراءة-معالجة-كتابة والمهام معًا لتشغيل وظيفة.

مثال Spring Batch

لنأخذ مثالًا عمليًا لتنفيذ Spring Batch. سننظر في السيناريو التالي لأغراض التنفيذ. يجب تحويل ملف CSV يحتوي على بيانات إلى XML مع البيانات والعلامات التي ستحمل اسم العمود. فيما يلي الأدوات والمكتبات الهامة المستخدمة في مثال Spring Batch.

  1. Apache Maven 3.5.0 – لبناء المشروع وإدارة التبعيات.
  2. Eclipse Oxygen Release 4.7.0 – بيئة التطوير المتكاملة لإنشاء تطبيقات Spring Batch باستخدام Maven.
  3. Java 1.8
  4. Spring Core 4.3.12.RELEASE
  5. Spring OXM 4.3.12.RELEASE
  6. Spring JDBC 4.3.12.RELEASE
  7. Spring Batch 3.0.8.RELEASE
  8. MySQL Java Driver 5.1.25 – استخدامه بناءً على تثبيت MySQL الخاص بك. هذا مطلوب لجداول بيانات Spring Batch metadata.

هيكلة دليل مثال Spring Batch

الصورة أدناه توضح جميع المكونات في مشروع مثال Spring Batch الخاص بنا.

تبعيات Spring Batch Maven

أدناه محتوى ملف pom.xml مع جميع التبعيات المطلوبة لمشروع مثال Spring Batch لدينا.

<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

تكوين الوظيفة لمعالجة دفعات الربيع

يجب علينا تعريف فولت الربيع ووظيفة دفعات الربيع في ملف تكوين. فيما يلي محتوى ملف 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 لمعالجة البيانات وكتابتها إلى ملف XML باستخدام StaxEventItemWriter.
  2. batch:job – تعريف هذا العلامة يحدد الوظيفة التي نريد إنشائها. يحدد خاصية Id معرف الوظيفة. يمكننا تعريف عدة وظائف في ملف xml واحد.
  3. batch:step – تستخدم هذه العلامة لتعريف خطوات مختلفة من وظيفة دفعة الربيع.
  4. تُقدم إطار Spring Batch Framework نوعين مختلفين من أساليب المعالجة ، وهما “توجيهة مهمة” و “توجيهة شظية”. ويُستخدم نمط توجيهة شظية في هذا المثال لقراءة البيانات واحدة تلو الأخرى وإنشاء “شظايا” سيتم كتابتها ، في حدود معاملة.
  5. reader: البن الربيعي المستخدم لقراءة البيانات. لقد استخدمنا حبة csvFileItemReader في هذا المثال وهي نموذج من FlatFileItemReader.
  6. processor: هذا هو الفصل الذي يستخدم لمعالجة البيانات. لقد استخدمنا CustomItemProcessor في هذا المثال.
  7. writer: البن المستخدم لكتابة البيانات في ملف XML.
  8. commit-interval: تحدد هذه الخاصية حجم الشظية التي ستتم تأكيدها عند الانتهاء من المعالجة. بشكل أساسي ، يعني أن ItemReader سيقرأ البيانات واحدة تلو الأخرى وسيقوم ItemProcessor أيضًا بمعالجتها بنفس الطريقة ، ولكن سيتم كتابة البيانات في ItemWriter فقط عندما يكون حجمها يساوي commit-interval.
  9. هناك ثلاثة واجهات مهمة يتم استخدامها كجزء من هذا المشروع هي ItemReader ، ItemProcessor و ItemWriter من حزمة org.springframework.batch.item.

نموذج Spring Batch للفئة

أولاً، نقوم بقراءة ملف CSV إلى كائن Java، ثم نستخدم \texttt{JAXB} لكتابته إلى ملف XML. فيما يلي نموذج الفئة الخاص بنا مع \texttt{التعليقات} الضرورية لـ JAXB. \texttt{

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
				+ "]";
	}

}

} يرجى ملاحظة أن حقول نموذج الفئة يجب أن تكون متطابقة مع تكوين محدد في ربيع الدفع، أي \texttt{property name="names" value="id,firstname,lastname,dob"} في حالتنا. \texttt{

} \texttt{Spring Batch FieldSetMapper} \texttt{} \texttt{

A custom FieldSetMapper is needed to convert a Date. If no data type conversion is required, then only BeanWrapperFieldSetMapper should be used to map the values by name automatically. The java class which extends FieldSetMapper is 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 {

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

		} // تنسيق الافتراضي yyyy-MM-dd \texttt{
		// fieldSet.readDate(4);
		String date = fieldSet.readString(3);
		try {
			report.setDob(dateFormat.parse(date));
		} catch (ParseException e) {
			e.printStackTrace();
		}

		return report;

	}

}

} \texttt{Spring Batch Item Processor} \texttt{} \texttt{

} الآن كما هو محدد في تكوين الوظيفة، سيتم تشغيل ItemProcessor قبل ItemWriter. لقد أنشأنا فئة \texttt{CustomItemProcessor.java} لنفس الغرض. \texttt{

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، كما يمكنك أن ترى أنني أقوم بتحويل قيم الاسم الأول والاسم الأخير إلى حالة الأحرف الكبيرة. \texttt{

} \texttt{ملفات تكوين ربيع} \texttt{}

في ملف تكوين دفعة الربيع الخاص بنا، قمنا بتوريد ملفي تكوين إضافيين – context.xml و database.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 في جدوله الفرعي الصحيح لـ Spring Batch.
  • transactionManager – يتحمل هذا المسؤولية عند التزام الصفقة بمجرد أن يتساوى حجم فاصل التزام والبيانات المعالجة.
  • jobLauncher – هذا هو قلب Spring Batch. يحتوي هذا الواجهة على طريقة التشغيل التي يتم استخدامها لتشغيل الوظيفة.
<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 بعض جداول البيانات الوسيطة لتخزين معلومات الوظائف الدفعية. يمكننا الحصول على إنشائها من تكوينات Spring Batch، ولكن من الأفضل القيام بذلك يدويًا عن طريق تنفيذ ملفات SQL، كما هو موضح في الشيفرة المعلقة أعلاه. من وجهة نظر الأمان، من الأفضل عدم منح صلاحية تنفيذ DDL لمستخدم قاعدة بيانات Spring Batch.

جداول Spring Batch

الجداول الخاصة بـ Spring Batch تتناسب بشكل وثيق مع كائنات النطاق التي تمثلها في لغة الجافا. على سبيل المثال – JobInstance، JobExecution، JobParameters وStepExecution تتطابق مع BATCH_JOB_INSTANCE، BATCH_JOB_EXECUTION، BATCH_JOB_EXECUTION_PARAMS وBATCH_STEP_EXECUTION على التوالي. ExecutionContext يتناغم مع كل من BATCH_JOB_EXECUTION_CONTEXT وBATCH_STEP_EXECUTION_CONTEXT. يعتبر JobRepository مسؤولاً عن حفظ وتخزين كل كائن جافا في جدوله الصحيح. فيما يلي تفاصيل كل جدول للبيانات الفوقية.

  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’s ExecutionContext. يوجد ExecutionContext واحد بالضبط لكل JobExecution، ويحتوي على جميع بيانات مستوى الوظيفة التي تحتاج إلى تحميلها بعد حدوث فشل حتى يمكن لـ JobInstance إعادة التشغيل من حيث فشل.
  6. Batch_step_execution_context: جدول BATCH_STEP_EXECUTION_CONTEXT يحتوي على البيانات ذات الصلة بسياق تنفيذ خطوة. هناك ExecutionContext واحد تمامًا لكل StepExecution، ويحتوي على جميع البيانات التي يجب أن تُحفظ لتنفيذ خطوة معينة. تمثل هذه البيانات عادة الحالة التي يجب استرجاعها بعد الفشل حتى يمكن لـ JobInstance إعادة البدء من حيث فشلت.
  7. Batch_job_execution_seq: يحتوي هذا الجدول على تسلسل تنفيذ البيانات للوظيفة.
  8. Batch_step_execution_seq: يحتوي هذا الجدول على البيانات لتسلسل تنفيذ الخطوة.
  9. Batch_job_seq: يحتوي هذا الجدول على البيانات لتسلسل الوظيفة في حال كان لدينا عدة وظائف سنحصل على عدة صفوف.

برنامج اختبار Spring Batch

مشروع مثال 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، يمكنك تنزيل المشروع النهائي من الرابط أدناه.

تنزيل مشروع مثال Spring Batch

المرجع: دليل رسمي

Source:
https://www.digitalocean.com/community/tutorials/spring-batch-example