viernes, 19 de diciembre de 2014

Unir ficheros mediante batch

Descripción del Problema

Cómo leer registros de múltiples ficheros (en este caso usaremos ficheros CSV), y escribir los diferentes registros en un sólo fichero csv.

Ficheros

Para ilustrar el caso, os proporciono 3 ficheros: domain-1-3-2013.csv, domain-2-3-2013.csv, domain-3-3-2013.csv

csv/inputs/domain-1-3-2013.csv

1
2
3
4
5
1,facebook.com
 
2,yahoo.com
 
3,google.com

csv/inputs/domain-2-3-2013.csv

1
2
3
4
5
200,wherever.com
 
300,stackoverflow.com
 
400,oracle.com

csv/inputs/domain-3-3-2013.csv

1
2
3
999,eclipse.org
 
888,baidu.com

CLASE 

Se requiere una clase que servirá para almacenar temporalmente los dos campos de cada linea del fichero.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ejemplo;
 
public class Domain {
 
 int id;
 String domain;
 
 public int getId() {
  return id;
 }
 
 public void setId(int id) {
  this.id = id;
 }
 
 public String getDomain() {
  return domain;
 }
 
 public void setDomain(String domain) {
  this.domain = domain;
 }
 
}

Configuración del Job de Spring

Solo se necesita la siguiente configuración en spring-batch

job-merge-files.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 ">
 
  <import resource="../config/context.xml">
 
  <bean class="com.ejemplo.Domain" id="domain">
 
  <job id="readMultiFileJob" xmlns="http://www.springframework.org/schema/batch">
    <step id="step1">
 <tasklet>
  <chunk commit-interval="1" reader="multiResourceReader" writer="flatFileItemWriter">
 </chunk></tasklet>
    </step>
  </job>
 
  <bean class=" org.springframework.batch.item.file.MultiResourceItemReader" id="multiResourceReader">
 <property name="resources" value="file:csv/inputs/domain-*.csv">
 <property name="delegate" ref="flatFileItemReader">
  </property></property></bean>
 
  <bean class="org.springframework.batch.item.file.FlatFileItemReader" id="flatFileItemReader">
 <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, domain">
      </property></bean>
  </property>
  <property name="fieldSetMapper">
      <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
    <property name="prototypeBeanName" value="domain">
      </property></bean>
  </property>
   </bean>
 </property>
  </bean>
 
  <bean class="org.springframework.batch.item.file.FlatFileItemWriter" id="flatFileItemWriter">
 <property name="resource" value="file:csv/outputs/domain.all.csv">
 <property name="appendAllowed" value="true">
 <property name="lineAggregator">
   <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
  <property name="delimiter" value=",">
  <property name="fieldExtractor">
    <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
   <property name="names" value="id, domain">
    </property></bean>
  </property>
   </property></bean>
 </property>
  </property></property></bean>
 
</bean></import></beans>

Configuración del Contexto de Spring-Batch

Este fichero suele cambiar según la configuración que quieras hacer con spring-batch, les facilito la parte más básica.

context.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
    <!-- stored job-meta in memory -->
    <bean class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" id="jobRepository">
 <property name="transactionManager" ref="transactionManager">
    </property></bean>
 
    <bean class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" id="transactionManager">
 
    <bean class="org.springframework.batch.core.launch.support.SimpleJobLauncher" id="jobLauncher">
 <property name="jobRepository" ref="jobRepository">
    </property></bean>
 
</bean></beans>


Clase para ejecutar el job

Se requiere una clase que servirá para ejecutar el job.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.ejemplo;
 
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) {
 App obj = new App();
 obj.run();
  }
 
  private void run() {
 
 String[] springConfig = { "spring/batch/jobs/job-merge-files.xml" };
 
 ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
 
 JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
 Job job = (Job) context.getBean("readMultiFileJob");
 
 try {
 
  JobExecution execution = jobLauncher.run(job, new JobParameters());
  System.out.println("Exit Status : " + execution.getStatus());
 
 } catch (Exception e) {
  e.printStackTrace();
 }
 
 System.out.println("Done");
 
  }
 
}

RESULTADO

Al ejecutar el batch, la salida esperada será un fichero llamado domain.all.csv con la combinación de los registros de los tres ficheros csv de entrada.

csv/outputs/domain.all.csv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1,facebook.com
 
2,yahoo.com
 
3,google.coms
 
200,mkyong.com
 
300,stackoverflow.com
 
400,oracle.com
 
999,eclipse.org
 
888,baidu.com

No hay comentarios:

Publicar un comentario