Spring · Fiche 08

Spring Batch

Le framework de traitement par lots de Spring : architecture Job/Step/Chunk et exemple complet reader-processor-writer avec listener.

Spring Batch est le framework de Spring pour le traitement par lots : import de fichiers, migrations de données, traitements de masse planifiés.

⚙️ Architecture Spring Batch

Concept Rôle Détail
Job Unité de traitement complète Contient un ou plusieurs Steps
Step Étape de traitement Reader, Processor, Writer
Chunk Traitement par blocs Optimisation des performances

📝 Exemple de Job batch

Configuration du Job

Le job enchaîne les steps ; chaque step lit, transforme et écrit les données par blocs (chunks).

@Configuration
@EnableBatchProcessing
public class BatchConfig {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job importUserJob() {
        return jobBuilderFactory.get("importUserJob")
            .incrementer(new RunIdIncrementer())
            .listener(jobExecutionListener())
            .start(step1())
            .next(step2())
            .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
            .<UserCsv, User>chunk(10)
            .reader(reader())
            .processor(processor())
            .writer(writer())
            .build();
    }

    @Bean
    public FlatFileItemReader<UserCsv> reader() {
        return new FlatFileItemReaderBuilder<UserCsv>()
            .name("userItemReader")
            .resource(new ClassPathResource("users.csv"))
            .delimited()
            .names(new String[]{"firstName", "lastName", "email"})
            .fieldSetMapper(new BeanWrapperFieldSetMapper<UserCsv>() {{
                setTargetType(UserCsv.class);
            }})
            .build();
    }

    @Bean
    public UserItemProcessor processor() {
        return new UserItemProcessor();
    }

    @Bean
    public JdbcBatchItemWriter<User> writer() {
        return new JdbcBatchItemWriterBuilder<User>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("INSERT INTO users (first_name, last_name, email) VALUES (:firstName, :lastName, :email)")
            .dataSource(dataSource)
            .build();
    }
}

Processor personnalisé

Le processor transforme chaque élément lu avant l’écriture.

@Component
public class UserItemProcessor implements ItemProcessor<UserCsv, User> {

    private static final Logger log = LoggerFactory.getLogger(UserItemProcessor.class);

    @Override
    public User process(final UserCsv userCsv) throws Exception {
        final String firstName = userCsv.getFirstName().toUpperCase();
        final String lastName = userCsv.getLastName().toUpperCase();
        final String email = userCsv.getEmail().toLowerCase();

        final User transformedUser = new User(firstName, lastName, email);

        log.info("Converting (" + userCsv + ") into (" + transformedUser + ")");

        return transformedUser;
    }
}

Listener pour surveiller l’exécution

Le listener est notifié aux étapes clés du cycle de vie du job (ici, à la fin).

@Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {

    private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);

    private final JdbcTemplate jdbcTemplate;

    @Override
    public void afterJob(JobExecution jobExecution) {
        if(jobExecution.getStatus() == BatchStatus.COMPLETED) {
            log.info("!!! JOB FINISHED! Time to verify the results");

            jdbcTemplate.query("SELECT first_name, last_name, email FROM users",
                (rs, row) -> new User(
                    rs.getString(1),
                    rs.getString(2),
                    rs.getString(3))
            ).forEach(user -> log.info("Found <" + user + "> in the database."));
        }
    }
}

Point clé : avec chunk(10), les éléments sont lus et transformés un par un, mais écrits par paquets de 10 dans une même transaction. C’est le compromis performance/reprise sur erreur au cœur de Spring Batch.

Interro écrite

7 questions pour valider la fiche. Une seule réponse par question.

Question 1/7Quelle est la hiérarchie des concepts dans Spring Batch ?