qingpan
qingpan

Reputation: 426

How to process multiple lines with the Spring Batch?

My data is like this

UserId,UserData

1,data11
1,data12
2,data21
3,data31

The question is, how I can make the spring batch itemreader read multiple lines and map to object like

Map < userid, List < userdata > >

Upvotes: 1

Views: 9774

Answers (1)

Braj
Braj

Reputation: 46841

Steps to follow:

  • create a custom Item Writer class by implementing ItemWriter where we have implemented a logic to store User object in Map<String,List<String>> because a single user can have multiple associated data

    package com.spring.batch.domain;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.springframework.batch.item.ItemWriter;
    
    public class UserItemWriter implements ItemWriter<User> {
    
        private static Map<String, List<String>> userMap = new HashMap<String, List<String>>();
    
        public void write(List<? extends User> users) throws Exception {
    
            for (User user : users) {
                List<String> list = userMap.get(user.getUserId());
                if (list == null) {
                    list = new ArrayList<String>();
                }
                list.add(user.getUserData());
                userMap.put(user.getUserId(), list);
            }
        }
    
        public static Map<String, List<String>> getUserMap() {
            return userMap;
        }
    
    }
    
  • Plain User POJO class having two fieldsuserId anduserData

    package com.spring.batch.domain;
    
    public class User {
        private String userId;
        private String userData;
    
        public String getUserId() {
            return userId;
        }
    
        public void setUserId(String userId) {
            this.userId = userId;
        }
    
        public String getUserData() {
            return userData;
        }
    
        public void setUserData(String userData) {
            this.userData = userData;
        }
    
    }
    
  • Main class to run the job

    package com.spring.batch.main;
    
    import java.util.List;
    import java.util.Map;
    
    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.JobParametersBuilder;
    import org.springframework.batch.core.JobParametersInvalidException;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
    import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
    import org.springframework.batch.core.repository.JobRestartException;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.spring.batch.domain.UserItemWriter;
    
    public class Main {
        public static void main(String[] args) throws BeansException,
                JobExecutionAlreadyRunningException, JobRestartException,
                JobInstanceAlreadyCompleteException, JobParametersInvalidException {
            ApplicationContext appContext = new ClassPathXmlApplicationContext(
                    "config/application-context.xml");
            JobLauncher jobLauncher = (JobLauncher) appContext.getBean("jobLauncher");
            jobLauncher.run(
                    (Job) appContext.getBean("job"),
                    new JobParametersBuilder().addString("input.file.name",
                            "file:src/main/resources/data/input.csv").toJobParameters());
    
            Map<String,List<String>> userMap=UserItemWriter.getUserMap();
            for (String userId : userMap.keySet()) {
                System.out.println(userId + ":" + userMap.get(userId));
            }
        }
    }
    
  • application-context.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...>
    
        <import resource="jobs.xml" />
    
        <bean id="jobLauncher"
            class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
            <property name="jobRepository" ref="jobRepository" />
        </bean>
    
        <bean id="jobRepository"
            class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
            <property name="transactionManager" ref="transactionManager" />
        </bean>
    
        <bean id="transactionManager"
            class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
    
    </beans>
    
  • jobs.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...>
    
        <job id="job" restartable="false"
            xmlns="http://www.springframework.org/schema/batch">
            <step id="step1">
                <tasklet>
                    <chunk reader="csvItemReader" writer="userItemWriter"
                        commit-interval="2">
                    </chunk>
                </tasklet>
            </step>
        </job>
    
        <bean id="userItemWriter" class="com.spring.batch.domain.UserItemWriter"
            scope="step" />
    
        <bean id="csvItemReader" class="org.springframework.batch.item.file.FlatFileItemReader"
            scope="step">
            <property name="lineMapper">
                <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                    <property name="lineTokenizer">
                        <bean
                            class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                            <property name="names" value="userId,userData" />
                        </bean>
                    </property>
                    <property name="fieldSetMapper">
                        <bean
                            class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
                            <property name="prototypeBeanName" value="user" />
                        </bean>
                    </property>
                </bean>
            </property>
            <property name="linesToSkip" value="1" />
            <property name="resource" value="#{jobParameters['input.file.name']}" />
        </bean>
    
        <bean id="user" class="com.spring.batch.domain.User" scope="prototype" />
    
    </beans>
    
  • input.csv

    UserId,UserData
    1,data11
    1,data12
    2,data21
    3,data31
    
  • Project structure screenshot (Maven) enter image description here

Upvotes: 2

Related Questions