Search code examples
javaspringspring-batchjavabeans

ItemReader Code repetition problem in spring batch


Suppose I have two classes implemented in the following manner (Ommiting job and chunk code for the sake of clarity). Each reader will have a delimiter based on the configuration class they are declared.

1) There is clearly code repetition that I would love to generalize in another class, because if I decide to add code in class*ItemReader(), I would have to do it across several files. How can that be accomplished? Not only the "delimiter" is important here, but the class inside <> in the itemReaderBuilder

2) How can I avoid having different names for each of the methods? the are basically the same, they just need a different class inside <> and a different delimiter.

3) How can I avoid the need to save the reader inside the configuration class to calculate the resource path in runtime (using a tasklet) as is date based.

@Configuration
public ClassA{

    @Value("${fileA.delimiter}")
    private String delimiter;

    private ItemReader reader;

    @Bean
    public ItemReader classAItemReader(){

            reader =  FlatFileItemReaderBuilder<ObjectA>
                .get("reader")
                .delimiter(this.delimiter)                
                .blabla()
                .build()

            return reader
    }
}

@Configuration
public ClassB{

    @Value("${fileB.delimiter}")
    private String delimiter;

    private ItemReader reader;

    @Bean
    public ItemReader classBItemReader(){

            reader =  FlatFileItemReaderBuilder<ObjectA>
                .get("reader")
                .delimiter(this.delimiter)                
                .blabla()
                .build()

            return reader
    }
}

Solution

  • One option I see for your use case is to define an abstract generic configuration class in which you define a generic reader. Here is an example:

    import org.springframework.batch.item.ItemReader;
    import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    
    public abstract class MyReaderConfiguration<T> {
    
        @Value("${file.delimiter}")
        private String delimiter;
    
        @Value("${file.columns}") // column1,column2
        private String fields;
    
        @Bean
        public ItemReader<T> itemReader(){
            return new FlatFileItemReaderBuilder<T>()
                    .name("reader")
                    .delimited()
                    .delimiter(delimiter)
                    .names(fields.split(","))
                    .build();
        }
    
    }
    

    Other parts of your job definition can go in a subclass with the desired target type (ObjectA or ObjectB).