Spring Batch - MySQL a XML

En este capítulo, crearemos una aplicación Spring Batch que usa un lector MySQL y un escritor XML.

Reader - El lector que estamos usando en la aplicación es JdbcCursorItemReader para leer datos de la base de datos MySQL.

Supongamos que hemos creado una tabla en la base de datos MySQL como se muestra a continuación:

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

Supongamos que hemos insertado los siguientes registros en él.

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 - El escritor que estamos usando en la aplicación es StaxEventItemWriter para escribir los datos en el archivo XML.

Processor - El procesador que estamos usando en la aplicación es un procesador personalizado que simplemente imprime los registros leídos del archivo CSV.

jobConfig.xml

A continuación se muestra el archivo de configuración de nuestra aplicación Spring Batch de muestra. En este archivo, definiremos el Trabajo y los Pasos. Además de estos, también definimos los beans para ItemReader, ItemProcessor y ItemWriter. (Aquí, los asociamos con sus respectivas clases y pasamos los valores de las propiedades requeridas para configurarlos).

<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

Lo siguiente es el context.xmlde nuestra aplicación Spring Batch. En este archivo, definiremos los beans como repositorio de trabajos, lanzador de trabajos y administrador de transacciones.

<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

A continuación se muestra la clase Procesador. En esta clase, escribimos el código de procesamiento en la aplicación. Aquí, estamos imprimiendo el contenido de cada registro.

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

Lo siguiente es el TutorialRowMapper clase que establece los datos en el Tutorial clase.

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

Lo siguiente es el Tutorialclase. Es una clase Java simple consetter y gettermétodos. En esta clase, usamos anotaciones para asociar los métodos de esta clase con las etiquetas del archivo 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

A continuación se muestra el código que inicia el proceso por lotes. En esta clase, lanzaremos la aplicación Batch ejecutando 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()); 
   } 
}

Al ejecutar esta aplicación, producirá el siguiente resultado.

May 08, 2017 11:32:06 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 
INFO: Refreshing org[email protected]3d646c37: 
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

Esto generará un archivo XML con el siguiente contenido.

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