Search code examples
javamongodbspring-batchetl

Using HashMap instead of POJO in JDBC batch Item Writer


I need to create a generic job that reads from mongodb collection, applies some logic of parsing and persists into sqlserver in spring batch. So instead of using a POJO for every table, i am planning to use a HashMap.

My question is it feasible to use HashMap in JdbcBatchItemWriter?

What about the sql query and the SqlParameterSourceProvider? what do i need to configure them ?


Solution

  • Yes that's possible. You can provide a custom ItemSqlParameterSourceProvider that returns a org.springframework.jdbc.core.namedparam.MapSqlParameterSource. Here is a quick self-contained example:

    import java.util.Arrays;
    import java.util.HashMap;
    
    import javax.sql.DataSource;
    
    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.JobParameters;
    import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
    import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
    import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.item.database.JdbcBatchItemWriter;
    import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
    import org.springframework.batch.item.support.ListItemReader;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
    
    @Configuration
    @EnableBatchProcessing
    public class SO68285708 {
    
        @Bean
        public ListItemReader<HashMap<String, String>> itemReader() {
            HashMap<String, String> item1 = new HashMap<>();
            item1.put("id", "1");
            item1.put("name", "foo");
            HashMap<String, String> item2 = new HashMap<>();
            item2.put("id", "2");
            item2.put("name", "bar");
            return new ListItemReader<>(Arrays.asList(item1, item2));
        }
    
        @Bean
        public JdbcBatchItemWriter<HashMap<String, String>> itemWriter() {
            String sql = "insert into person (id, name) values (:id, :name)";
            return new JdbcBatchItemWriterBuilder<HashMap<String, String>>()
                    .dataSource(dataSource())
                    .sql(sql)
                    .itemSqlParameterSourceProvider(item -> {
                        MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
                        mapSqlParameterSource.addValues(item);
                        return mapSqlParameterSource;
                    })
                    .build();
        }
    
        @Bean
        public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
            return jobs.get("job")
                    .start(steps.get("step")
                            .<HashMap<String, String>, HashMap<String, String>>chunk(5)
                            .reader(itemReader())
                            .writer(itemWriter())
                            .build())
                    .build();
        }
    
        public static void main(String[] args) throws Exception {
            ApplicationContext context = new AnnotationConfigApplicationContext(SO68285708.class);
            JobLauncher jobLauncher = context.getBean(JobLauncher.class);
            Job job = context.getBean(Job.class);
            jobLauncher.run(job, new JobParameters());
    
            DataSource dataSource = context.getBean(DataSource.class);
            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            Integer persons = jdbcTemplate.queryForObject("select count(*) from person", Integer.class);
            System.out.println("Persons in db = " + persons);
        }
    
        @Bean
        public DataSource dataSource() {
            EmbeddedDatabase embeddedDatabase = new EmbeddedDatabaseBuilder()
                    .setType(EmbeddedDatabaseType.HSQL)
                    .addScript("/org/springframework/batch/core/schema-hsqldb.sql")
                    .build();
            JdbcTemplate jdbcTemplate = new JdbcTemplate(embeddedDatabase);
            jdbcTemplate.execute("create table person (id int primary key, name varchar(20));");
            return embeddedDatabase;
        }
    
        public static class Person {
            private int id;
            private String name;
    
            public Person() {
            }
    
            public int getId() {
                return id;
            }
    
            public void setId(int id) {
                this.id = id;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public String toString() {
                return "Person{id=" + id + ", name='" + name + '\'' + '}';
            }
        }
    
    }