Einführung in Spring Batch

Einführung in Spring Batch

1. Einführung

In diesem Artikel konzentrieren wir uns auf eine praktische, codeorientierte Einführung in Spring Batch. Spring Batch ist ein Verarbeitungsframework für die robuste Ausführung von Jobs.

Es ist die aktuelle Version 3.0, die Spring 4 und Java 8 unterstützt. Es unterstützt auch JSR-352, eine neue Java-Spezifikation für die Stapelverarbeitung.

Here areind einige interessante und praktische Anwendungsfälle des Frameworks.

2. Workflow-Grundlagen

image

Spring Batch folgt der traditionellen Batch-Architektur, bei der ein Job-Repository für die Planung und Interaktion mit dem Job zuständig ist.

Ein Job kann mehr als einen Schritt haben - und jeder Schritt folgt normalerweise der Abfolge des Lesens, Verarbeitens und Schreibens von Daten.

Und natürlich wird das Framework hier den größten Teil der Arbeit für uns erledigen - insbesondere wenn es um die Persistenzarbeit auf niedriger Ebene beim Umgang mit Jobs geht - undsqlite für das Job-Repository verwenden.

2.1. Unser Beispiel Usecase

Der einfache Anwendungsfall, den wir hier angehen werden, ist: Wir werden einige Finanztransaktionsdaten von CSV nach XML migrieren.

Die Eingabedatei hat eine sehr einfache Struktur - sie enthält eine Transaktion pro Zeile, bestehend aus: einem Benutzernamen, der Benutzer-ID, dem Datum der Transaktion und dem Betrag:

username, userid, transaction_date, transaction_amount
devendra, 1234, 31/10/2015, 10000
john, 2134, 3/12/2015, 12321
robin, 2134, 2/02/2015, 23411

3. Der Maven POM

Für dieses Projekt erforderliche Abhängigkeiten sind Federkern, Federbatch undsqlite JDBC-Anschluss:


    4.0.0

    org.example
    spring-batch-intro
    0.1-SNAPSHOT
    jar

    spring-batch-intro
    http://maven.apache.org

    
        UTF-8
        4.2.0.RELEASE
        3.0.5.RELEASE
        3.8.11.2
    

    
        
        
            org.xerial
            sqlite-jdbc
            ${sqlite.version}
        
        
            org.springframework
            spring-oxm
            ${spring.version}
        
        
            org.springframework
            spring-jdbc
            ${spring.version}
        
        
            org.springframework.batch
            spring-batch-core
            ${spring.batch.version}
        
    

4. Spring Batch Config

Als erstes konfigurieren wir Spring Batch mit XML:



    
    
        
        
        
        
    

    
    
        
        
    

    
    

    
    
        
        
        
    

    

    
        
    

Natürlich steht auch eine Java-Konfiguration zur Verfügung:

@Configuration
@EnableBatchProcessing
public class SpringConfig {

    @Value("org/springframework/batch/core/schema-drop-sqlite.sql")
    private Resource dropReopsitoryTables;

    @Value("org/springframework/batch/core/schema-sqlite.sql")
    private Resource dataReopsitorySchema;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.sqlite.JDBC");
        dataSource.setUrl("jdbc:sqlite:repository.sqlite");
        return dataSource;
    }

    @Bean
    public DataSourceInitializer dataSourceInitializer(DataSource dataSource)
      throws MalformedURLException {
        ResourceDatabasePopulator databasePopulator =
          new ResourceDatabasePopulator();

        databasePopulator.addScript(dropReopsitoryTables);
        databasePopulator.addScript(dataReopsitorySchema);
        databasePopulator.setIgnoreFailedDrops(true);

        DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource);
        initializer.setDatabasePopulator(databasePopulator);

        return initializer;
    }

    private JobRepository getJobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(dataSource());
        factory.setTransactionManager(getTransactionManager());
        factory.afterPropertiesSet();
        return (JobRepository) factory.getObject();
    }

    private PlatformTransactionManager getTransactionManager() {
        return new ResourcelessTransactionManager();
    }

    public JobLauncher getJobLauncher() throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(getJobRepository());
        jobLauncher.afterPropertiesSet();
        return jobLauncher;
    }
}

5. Spring Batch Job Config

Schreiben wir nun unsere Jobbeschreibung für die CSV-zu-XML-Arbeit:



    

    
    

        

        
            
                
                    
                        
                    
                
                
                    
                
            
        
    

    

    
        
        
        
    

    
        
            
                org.example.spring_batch_intro.model.Transaction
            
        
    
    
        
            
                
                
            
        
    

Und natürlich die ähnliche Java-basierte Jobkonfiguration:

public class SpringBatchConfig {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Value("input/record.csv")
    private Resource inputCsv;

    @Value("file:xml/output.xml")
    private Resource outputXml;

    @Bean
    public ItemReader itemReader()
      throws UnexpectedInputException, ParseException {
        FlatFileItemReader reader = new FlatFileItemReader();
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        String[] tokens = { "username", "userid", "transactiondate", "amount" };
        tokenizer.setNames(tokens);
        reader.setResource(inputCsv);
        DefaultLineMapper lineMapper =
          new DefaultLineMapper();
        lineMapper.setLineTokenizer(tokenizer);
        lineMapper.setFieldSetMapper(new RecordFieldSetMapper());
        reader.setLineMapper(lineMapper);
        return reader;
    }

    @Bean
    public ItemProcessor itemProcessor() {
        return new CustomItemProcessor();
    }

    @Bean
    public ItemWriter itemWriter(Marshaller marshaller)
      throws MalformedURLException {
        StaxEventItemWriter itemWriter =
          new StaxEventItemWriter();
        itemWriter.setMarshaller(marshaller);
        itemWriter.setRootTagName("transactionRecord");
        itemWriter.setResource(outputXml);
        return itemWriter;
    }

    @Bean
    public Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setClassesToBeBound(new Class[] { Transaction.class });
        return marshaller;
    }

    @Bean
    protected Step step1(ItemReader reader,
      ItemProcessor processor,
      ItemWriter writer) {
        return steps.get("step1"). chunk(10)
          .reader(reader).processor(processor).writer(writer).build();
    }

    @Bean(name = "firstBatchJob")
    public Job job(@Qualifier("step1") Step step1) {
        return jobs.get("firstBatchJob").start(step1).build();
    }
}

OK, jetzt, da wir die gesamte Konfiguration haben, wollen wir sie aufschlüsseln und mit der Diskussion beginnen.

5.1. Lesen Sie Daten und erstellen Sie Objekte mitItemReader

Zuerst haben wir diecvsFileItemReader konfiguriert, die die Daten aus denrecord.csv lesen und in dasTransaction-Objekt konvertieren:

@SuppressWarnings("restriction")
@XmlRootElement(name = "transactionRecord")
public class Transaction {
    private String username;
    private int userId;
    private Date transactionDate;
    private double amount;

    /* getters and setters for the attributes */

    @Override
    public String toString() {
        return "Transaction [username=" + username + ", userId=" + userId
          + ", transactionDate=" + transactionDate + ", amount=" + amount
          + "]";
    }
}

Dazu wird ein benutzerdefinierter Mapper verwendet:

public class RecordFieldSetMapper implements FieldSetMapper {

    public Transaction mapFieldSet(FieldSet fieldSet) throws BindException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
        Transaction transaction = new Transaction();

        transaction.setUsername(fieldSet.readString("username"));
        transaction.setUserId(fieldSet.readInt(1));
        transaction.setAmount(fieldSet.readDouble(3));
        String dateString = fieldSet.readString(2);
        try {
            transaction.setTransactionDate(dateFormat.parse(dateString));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return transaction;
    }
}

5.2. Daten mitItemProcessor verarbeiten

Wir haben unseren eigenen ArtikelprozessorCustomItemProcessor erstellt. Dadurch wird nichts verarbeitet, was mit dem Transaktionsobjekt zusammenhängt. Es wird lediglich das ursprüngliche Objekt vom Leser an den Schreiber übergeben:

public class CustomItemProcessor implements ItemProcessor {

    public Transaction process(Transaction item) {
        return item;
    }
}

5.3. Schreiben von Objekten in den FS mitItemWriter

Schließlich werden wir diesetransaction in einer XML-Datei speichern, die sich unterxml/output.xml befindet:


    
    
    

5.4. Stapeljob konfigurieren

Alles was wir tun müssen, ist die Punkte mit einem Job zu verbinden - unter Verwendung derbatch:job-Syntax.

Beachten Sie diecommit-interval - das ist die Anzahl der Transaktionen, die gespeichert werden müssen, bevor der Stapel anitemWriter übergeben wird. Die Transaktionen werden bis zu diesem Zeitpunkt im Speicher gespeichert (oder bis das Ende der Eingabedaten erreicht ist):


    
        
            
            
        
    

5.5. Ausführen des Stapeljobs

Das war's - jetzt richten wir alles ein und führen es aus:

public class App {
    public static void main(String[] args) {
        // Spring Java config
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(SpringConfig.class);
        context.register(SpringBatchConfig.class);
        context.refresh();

        JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
        Job job = (Job) context.getBean("firstBatchJob");
        System.out.println("Starting the batch job");
        try {
            JobExecution execution = jobLauncher.run(job, new JobParameters());
            System.out.println("Job Status : " + execution.getStatus());
            System.out.println("Job completed");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Job failed");
        }
    }
}

6. Fazit

Dieses Tutorial gibt Ihnen eine grundlegende Vorstellung vonhow to work with Spring Batch und wie Sie es in einem einfachen Anwendungsfall verwenden können.

Es wird gezeigt, wie Sie Ihre Stapelverarbeitungs-Pipeline auf einfache Weise entwickeln und wie Sie verschiedene Phasen des Lesens, Verarbeitens und Schreibens anpassen können.

Diefull implementation dieses Tutorials finden Sie inthe github project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.