34
votes

I am currently writing a Spring batch where I am reading a chunk of data, processing it and then I wish to pass this data to 2 writers. One writer would simply update the database whereas the second writer will write to a csv file.

I am planning to write my own custom writer and inject the two itemWriters in the customItemWriter and call the write methods of both the item writers in the write method of customItemWriter. Is this approach correct? Are there any ItemWriter implementations available which meet my requirements?

Thanks in advance

6

6 Answers

26
votes

You can use Spring's CompositeItemWriter and delegate to it all your writers.
here is a configuration example.

15
votes

You don't necessarily have to use xml like the example. If the rest of your code uses annotation, you could simply do the following.

public ItemWriter<T> writerOne(){
    ItemWriter<T> writer = new ItemWriter<T>();
    //your logic here
    return writer;
}

public ItemWriter<T> writerTwo(){
    ItemWriter<T> writer = new ItemWriter<T>();
    //your logic here
    return writer;
}

public CompositeItemWriter<T> compositeItemWriter(){
    CompositeItemWriter writer = new CompositeItemWriter();
    writer.setDelegates(Arrays.asList(writerOne(),writerTwo()));
    return writer;
}
4
votes

You were right. SB is heavly based on delegation so using a CompositeItemWriter is the right choice for your needs.

2
votes

Java Config way SpringBatch4

@Bean
    public Step step1() {
            return this.stepBuilderFactory.get("step1")
                                    .<String, String>chunk(2)
                                    .reader(itemReader())
                                    .writer(compositeItemWriter())
                                    .stream(fileItemWriter1())
                                    .stream(fileItemWriter2())
                                    .build();
    }

    /**
     * In Spring Batch 4, the CompositeItemWriter implements ItemStream so this isn't
     * necessary, but used for an example.
     */
    @Bean
    public CompositeItemWriter compositeItemWriter() {
            List<ItemWriter> writers = new ArrayList<>(2);
            writers.add(fileItemWriter1());
            writers.add(fileItemWriter2());

            CompositeItemWriter itemWriter = new CompositeItemWriter();

            itemWriter.setDelegates(writers);

            return itemWriter;
    }
0
votes

Depending on your need, another option is to extend the Writer class and add functionality there. For example, I have a project where I am extending HibernateItemWriter and then overriding write(List items). I then send the objects I am writing along with my sessionFactory to the doWrite method of the Writer: doWrite(sessionFactory, filteredRecords).

So in the example above, I could write to the csv file in my extended class and then the HibernateItemWriter would write to the database. Obviously this might not be ideal for this example, but for certain scenarios it is a nice option.

0
votes

Here's a possible solution. Two writers inside a Composite Writer.

    @Bean
public JdbcBatchItemWriter<XPTO> writer(DataSource dataSource) {        
    return new JdbcBatchItemWriterBuilder<XPTO>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("UPDATE xxxx")
            .dataSource(dataSource)             
            .build();
}

@Bean
public JdbcBatchItemWriter<XPTO> writer2(DataSource dataSource) {           
    return new JdbcBatchItemWriterBuilder<XPTO>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("UPDATE yyyyy")
            .dataSource(dataSource)             
            .build();
}

@Bean
public CompositeItemWriter<XPTO> compositeItemWriter(DataSource dataSource) {
    CompositeItemWriter<XPTO> compositeItemWriter = new CompositeItemWriter<>();
    compositeItemWriter.setDelegates(Arrays.asList( writer(dataSource), writer2(dataSource)));
    return compositeItemWriter;
}

@Bean
protected Step step1(DataSource datasource) {
    
    return this.stepBuilderFactory.get("step1").
            <XPTO, XPTO>chunk(1).
            reader(reader()).
            processor(processor()).             
            writer(compositeItemWriter(datasource)).
            build();

}