Spring Batch: da MySQL a XML

In questo capitolo creeremo un'applicazione Spring Batch che utilizza un lettore MySQL e un writer XML.

Reader - Il lettore che stiamo utilizzando nell'applicazione è JdbcCursorItemReader per leggere i dati dal database MySQL.

Supponiamo di aver creato una tabella nel database MySQL come mostrato di seguito -

CREATE TABLE details.xml_mysql( 
   person_id int(10) NOT NULL, 
   sales VARCHAR(20), 
   qty int(3), 
   staffName VARCHAR(20), 
   date VARCHAR(20) 
);

Supponiamo di aver inserito i seguenti record in esso.

mysql> select * from tutorialsdata; 
+-------------+-----------------+----------------+-----------------+ 
| tutorial_id | tutorial_author | tutorial_title | submission_date | 
+-------------+-----------------+----------------+-----------------+ 
|         101 | Sanjay          | Learn Java     | 06-05-2007      | 
|         102 | Abdul S         | Learn MySQL    | 19-04-2007      | 
|         103 | Krishna Kasyap  | Learn JavaFX   | 06-07-2017      | 
+-------------+-----------------+----------------+-----------------+ 
3 rows in set (0.00 sec)

Writer - Il writer che stiamo utilizzando nell'applicazione è StaxEventItemWriter per scrivere i dati nel file XML.

Processor - Il processore che stiamo utilizzando nell'applicazione è un processore personalizzato che stampa solo i record letti dal file CSV.

jobConfig.xml

Di seguito è riportato il file di configurazione della nostra applicazione Spring Batch di esempio. In questo file definiremo il lavoro e i passaggi. Oltre a questi, definiamo anche i bean per ItemReader, ItemProcessor e ItemWriter. (Qui, li associamo alle rispettive classi e passiamo i valori per le proprietà richieste per configurarli.)

<beans xmlns = "http://www.springframework.org/schema/beans" 
   xmlns:batch = "http://www.springframework.org/schema/batch" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:util = "http://www.springframework.org/schema/util" 
   xsi:schemaLocation = " http://www.springframework.org/schema/batch 
      http://www.springframework.org/schema/batch/spring-batch-2.2.xsd 
      http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">  
   
   <import resource = "../jobs/context.xml" /> 
  
   <bean id = "report" class = "Report" scope = "prototype" /> 
   <bean id = "itemProcessor" class = "CustomItemProcessor" />  
   
   <batch:job id = "helloWorldJob"> 
      <batch:step id = "step1"> 
         <batch:tasklet> 
            <batch:chunk reader = "dbItemReader" 
               writer = "mysqlItemWriter" processor = "itemProcessor" commit-interval = "10">
            </batch:chunk> 
         </batch:tasklet> 
      </batch:step> 
   </batch:job> 
         
   <bean id = "dbItemReader" 
      class = "org.springframework.batch.item.database.JdbcCursorItemReader" scope = "step"> 
      <property name = "dataSource" ref = "dataSource" /> 
      <property name = "sql" value = "select * from tutorials_data" /> 
      <property name = "rowMapper"> 
         <bean class = "TutorialRowMapper" /> 
      </property> 
   </bean>             
   <bean id = "mysqlItemWriter" 
      class = "org.springframework.batch.item.xml.StaxEventItemWriter"> 
      <property name = "resource" value = "file:xml/outputs/tutorials.xml" /> 
      <property name = "marshaller" ref = "reportMarshaller" />
      <property name = "rootTagName" value = "Tutorial" /> 
   </bean>  
   
   <bean id = "reportMarshaller" class = "org.springframework.oxm.jaxb.Jaxb2Marshaller"> 
      <property name = "classesToBeBound"> 
         <list> 
            <value>Tutorial</value> 
         </list> 
      </property> 
   </bean> 
</beans>

Context.xml

Di seguito è riportato il file context.xmldella nostra applicazione Spring Batch. In questo file, definiremo i bean come il repository dei lavori, il lanciatore di lavori e il gestore delle transazioni.

<beans xmlns = " http://www.springframework.org/schema/beans" 
   xmlns:jdbc = "http://www.springframework.org/schema/jdbc" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation = "http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
      http://www.springframework.org/schema/jdbc 
      http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd "> 
   
   <!-- 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.ResourcelessTransactionMana ger" />  
   <bean id = "jobLauncher"
      class = "org.springframework.batch.core.launch.support.SimpleJobLauncher"> 
      <property name = "jobRepository" ref = "jobRepository" /> 
   </bean> 
  
   <!-- connect to MySQL 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/details" /> 
      <property name = "username" value = "myuser" /> 
      <property name = "password" value = "password" /> 
   </bean> 
  
   <!-- 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>

CustomItemProcessor.java

Di seguito è riportata la classe Processor. In questa classe, scriviamo il codice di elaborazione nell'applicazione. Qui stiamo stampando il contenuto di ogni record.

import org.springframework.batch.item.ItemProcessor;  

public class CustomItemProcessor implements ItemProcessor<Tutorial, Tutorial> {  

   @Override 
   public Tutorial process(Tutorial item) throws Exception { 
      System.out.println("Processing..." + item); 
      return item; 
   } 
}

TutorialRowMapper.java

Di seguito è riportato il file TutorialRowMapper classe che imposta i dati su Tutorial classe.

import java.sql.ResultSet; 
import java.sql.SQLException; 
import org.springframework.jdbc.core.RowMapper;  

public class TutorialRowMapper implements RowMapper<Tutorial> {  
   
   @Override 
   public Tutorial mapRow(ResultSet rs, int rowNum) throws SQLException {  
      
      Tutorial tutorial = new Tutorial();  
      tutorial.setTutorial_id(rs.getInt("tutorial_id")); 
      tutorial.setTutorial_author(rs.getString("tutorial_author")); 
      tutorial.setTutorial_title(rs.getString("tutorial_title")); 
      tutorial.setSubmission_date(rs.getString("submission_date"));  
      return tutorial; 
   } 
}

Tutorial.java

Di seguito è riportato il file Tutorialclasse. È una semplice classe Java consetter e gettermetodi. In questa classe, stiamo usando le annotazioni per associare i metodi di questa classe ai tag del file XML.

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

@XmlRootElement(name = "details") 
public class Tutorial {  
   
   int tutorial_id; 
   String tutorial_author;
   String submission_date; 
  
   @XmlAttribute(name = "tutorial_id") 
   public int getTutorial_id() { 
      return tutorial_id; 
   }  
   
   public void setTutorial_id(int tutorial_id) { 
      this.tutorial_id = tutorial_id; 
   }  
 
   @XmlElement(name = "tutorial_author") 
   public String getTutorial_author() { 
      return tutorial_author; 
   }  
   
   public void setTutorial_author(String tutorial_author) { 
      this.tutorial_author = tutorial_author; 
   }  
 
   @XmlElement(name = "tutorial_title") 
   public String getTutorial_title() { 
      return tutorial_title; 
   } 
  
   public void setTutorial_title(String tutorial_title) { 
      this.tutorial_title = tutorial_title; 
   }  
 
   @XmlElement(name = "submission_date") 
   public String getSubmission_date() { 
      return submission_date; 
   }

   public void setSubmission_date(String submission_date) { 
      this.submission_date = submission_date; 
   }  

   public String toString() { 
      return " [Tutorial Id=" + tutorial_id + ", 
      Tutorial Author =" + tutorial_author  + ", 
      Tutorial Title =" + tutorial_title + ", 
      Submission Date =" + submission_date + "]"; 
   } 
}

App.java

Di seguito è riportato il codice che avvia il processo batch. In questa classe, lanceremo l'applicazione batch eseguendo JobLauncher.

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

public class App {  
   public static void main(String[] args) throws Exception { 
     
      String[] springConfig  =  { "jobs/job_hello_world.xml" };  
      
      // Creating the application context object  
      ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);  
      
      // Creating the job launcher 
      JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); 
    
      // Creating the job 
      Job job = (Job) context.getBean("helloWorldJob");
      
      // Executing the JOB 
      JobExecution execution = jobLauncher.run(job, new JobParameters()); 
      System.out.println("Exit Status : " + execution.getStatus()); 
   } 
}

Quando si esegue questa applicazione, produrrà il seguente output.

May 08, 2017 11:32:06 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 
INFO: Refreshing org.springframework.context.support.Cl[email protected]: 
startup date [Mon May 08 11:32:06 IST 2017]; root of context hierarchy 
May 08, 2017 11:32:06 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 
INFO: Loading XML bean definitions from class path resource [jobs/job_hello_world.xml] 
May 08, 2017 11:32:07 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions   
May 08, 2017 11:32:14 AM org.springframework.batch.core.job.SimpleStepHandler handleStep 
INFO: Executing step: [step1] 
Processing... [Tutorial Id=101, Tutorial Author=Sanjay, 
Tutorial Title=Learn Java, Submission Date=06-05-2007] 
Processing... [Tutorial Id=102, Tutorial Author=Abdul S, 
Tutorial Title=Learn MySQL, Submission Date=19-04-2007] 
Processing... [Tutorial Id=103, Tutorial Author=Krishna Kasyap, 
Tutorial Title=Learn JavaFX, Submission Date=06-07-2017] 
May 08, 2017 11:32:14 AM org.springframework.batch.core.launch.support.SimpleJobLauncher run 
INFO: Job: [FlowJob: [name=helloWorldJob]] completed with the following parameters: 
[{}] and the following status: [COMPLETED] 
Exit Status : COMPLETED

Questo genererà un file XML con i seguenti contenuti.

<?xml version = "1.0" encoding = "UTF-8"?> 
<Tutorial> 
   <details tutorial_id = "101"> 
      <submission_date>06-05-2007</submission_date> 
      <tutorial_author>Sanjay</tutorial_author> 
      <tutorial_title>Learn Java</tutorial_title> 
   </details> 
   
   <details tutorial_id = "102"> 
      <submission_date>19-04-2007</submission_date> 
      <tutorial_author>Abdul S</tutorial_author> 
      <tutorial_title>Learn MySQL</tutorial_title> 
   </details>  
   
   <details tutorial_id = "103"> 
      <submission_date>06-07-2017</submission_date> 
      <tutorial_author>Krishna Kasyap</tutorial_author> 
      <tutorial_title>Learn JavaFX</tutorial_title> 
   </details> 
</Tutorial>