Introduction au lot de printemps

Introduction au lot de printemps

1. introduction

Dans cet article, nous allons nous concentrer sur une introduction pratique et axée sur le code à Spring Batch. Spring Batch est une infrastructure de traitement conçue pour une exécution robuste des travaux.

Il s'agit de la version 3.0 actuelle, qui prend en charge Spring 4 et Java 8. Il prend également en charge JSR-352, qui est une nouvelle spécification Java pour le traitement par lots.

Here are quelques cas d'utilisation intéressants et pratiques du framework.

2. Principes de base du workflow

image

Spring batch est conforme à l'architecture de lot traditionnelle dans laquelle un référentiel de travaux effectue le travail de planification et d'interaction avec le travail.

Une tâche peut comporter plusieurs étapes - et chaque étape suit généralement la séquence de lecture, de traitement et d'écriture des données.

Et bien sûr, le framework fera le gros du travail pour nous ici - en particulier en ce qui concerne le travail de persistance de bas niveau de gestion des jobs - en utilisantsqlite pour le référentiel de jobs.

2.1. Notre exemple d'utilisation

Le cas d'utilisation simple que nous allons aborder ici est le suivant: nous allons migrer certaines données de transactions financières de CSV vers XML.

Le fichier d'entrée a une structure très simple: il contient une transaction par ligne, composée de: un nom d'utilisateur, l'identifiant de l'utilisateur, la date de la transaction et le montant:

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. Le Maven POM

Les dépendances requises pour ce projet sont spring core, spring batch etsqlite jdbc connector:


    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. Configuration du lot de printemps

La première chose que nous allons faire est de configurer Spring Batch avec XML:



    
    
        
        
        
        
    

    
    
        
        
    

    
    

    
    
        
        
        
    

    

    
        
    

Bien sûr, une configuration Java est également disponible:

@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. Configuration du travail par lots de printemps

Écrivons maintenant notre description de poste pour le travail CSV vers XML:



    

    
    

        

        
            
                
                    
                        
                    
                
                
                    
                
            
        
    

    

    
        
        
        
    

    
        
            
                org.example.spring_batch_intro.model.Transaction
            
        
    
    
        
            
                
                
            
        
    

Et bien sûr, la configuration de travail similaire basée sur Java:

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, maintenant que nous avons la configuration complète, décomposons-la et commençons à en discuter.

5.1. Lire des données et créer des objets avecItemReader

Nous avons d'abord configuré lescvsFileItemReader qui liront les données desrecord.csv et les convertiront en objetTransaction:

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

Pour ce faire, il utilise un mappeur personnalisé:

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. Traitement des données avecItemProcessor

Nous avons créé notre propre processeur d'élément,CustomItemProcessor. Cela ne traite rien lié à l'objet de transaction - il ne fait que transmettre l'objet d'origine provenant du lecteur au rédacteur:

public class CustomItemProcessor implements ItemProcessor {

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

5.3. Ecriture d'objets sur le FS avecItemWriter

Enfin, nous allons stocker cetransaction dans un fichier xml situé àxml/output.xml:


    
    
    

5.4. Configuration du travail par lots

Donc, tout ce que nous avons à faire est de relier les points à un travail - en utilisant la syntaxebatch:job.

Notez lescommit-interval - c'est le nombre de transactions à conserver en mémoire avant de valider le lot vers lesitemWriter; il gardera les transactions en mémoire jusqu'à ce point (ou jusqu'à ce que la fin des données d'entrée soit rencontrée):


    
        
            
            
        
    

5.5. Exécution du travail par lots

C'est tout - configurons et exécutons maintenant tout:

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. Conclusion

Ce tutoriel vous donne une idée de base dehow to work with Spring Batch et comment l'utiliser dans un cas d'utilisation simple.

Il montre comment vous pouvez facilement développer votre pipeline de traitement par lots et comment personnaliser différentes étapes de lecture, de traitement et d’écriture.

Lesfull implementation de ce didacticiel se trouvent dansthe github project - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.