javaxmlspring-mvcspring-batchflatfilereader

FlatFileItemReader and FlatFileItemWriter Using Batch Job


I want to read, parse and write data from text file. I achieved this but I have a question about the declaration of the identifiers. I show my class below.

CustomerDataMapper.java

public class CustomerDataMapper implements FieldSetMapper<Customer> {
     private static Log log = LogFactory.getLog(CustomerDataMapper.class);

     @Override
     public RawData mapFieldSet(FieldSet fieldSet) throws BindException {
          Customer customer = new Customer();

          try {
              customer.setCustNum(fieldSet.readInt("id"));
              customer.setNameSurname(fieldSet.readString("name"));
          }catch (Exception e){
              log.error("Mapping error, " + e);
          }
          return customer;
     }
}

Customer.java

@Component
@Scope("prototype")
public class Customer implements Serializable{
    private Integer custNum;
    private String nameSurname;

    public String getCustNum) {
        return custNum;
    }

    public void setCustNum(String custNum) {
        this.custNum = custNum;
    }

    public String getNameSurname() {
        return nameSurname;
    }

    public void setNameSurname(String nameSurname) {
        this.nameSurname = nameSurname;
    }
}

fileManager.xml

<bean id="customerFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
    <property name= "encoding" value ="Cp1254"/>                           
    <property name="resource" value="#{jobParameters['processPath']}"/>
    <property name="lineMapper">
    <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
        <property name="lineTokenizer">
            <bean class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
                <property name="names" value="id, name"/>
                <property name="columns" ref="1-9, 10-30"/>
            </bean>
        </property>
        <property name="fieldSetMapper">
            <bean class="mapper.CustomerDataMapper ">
                <property name="fileName" value="#{jobParameters['fileNameAndTime']}"/>
            </bean>
        </property>
    </bean>
</property>

<bean id="customerFileItemWriter" class="org.springframework.batch.item.database.JdbcBatchItemWriter">
    <property name="dataSource" ref="dataSource"/>
    <property name="sql">
        <value>
            <![CDATA[
                insert into CUSTOMER(CUST_NUM, NAME_SURNAME)
                values (:custNum, :nameSurname)
            ]]>
        </value>
    </property>
    <property name="itemSqlParameterSourceProvider">
    <bean class="org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider"/>
</property>

 <bean id = "customerSkipListener" class="listener.CustomerSkipListener"/>

<batch:job id="customerImportJob">
    <batch:step id="smsPasswordDataImportStep">
        <batch:tasklet>
            <batch:chunk reader="customerFileItemReader" writer="customerFileItemWriter" commit-interval="1" skip-limit="1000">
            <batch:skippable-exception-classes>
                <batch:include class="java.lang.Exception"/>
                <batch:exclude class="java.io.FileNotFoundException"/>
            </batch:skippable-exception-classes> <batch:listeners>
            <batch:listener ref="customerSkipListener" />
        </batch:listeners>
        </batch:chunk>
    </batch:tasklet>

</batch:step>

I want to ask this, Can I use this class below for FieldSetMapper. If I don't use Camelcase notation when defining identifiers, it doesn't work to write to database. Is there any approach or method about this. Thank you so much.

Customer.java

@Component
@Scope("prototype")
public class Customer implements Serializable{

private Integer Cust_Num;
private String Name_Surname;

public String getCust_Num() {
    return Cust_Num;
}

public void setCust_Num(String Cust_Num) {
    this.Cust_Num = Cust_Num;
}

public String getName_Surname() {
    return Name_Surname;
}

public void setName_Surname(String Name_Surname) {
    this.Name_Surname = Name_Surname;
}
}

Solution

  • I want to ask this, Can use this class for FieldSetMapper

    Yes you can use this class. Since you wrote your custom FieldSetMapper, you have control over the mapping logic. Your mapper would be something like:

    public class CustomerDataMapper implements FieldSetMapper<Customer> {
       private static Log log = LogFactory.getLog(CustomerDataMapper.class);
    
       @Override
       public RawData mapFieldSet(FieldSet fieldSet) throws BindException {
          Customer customer = new Customer();
    
          try {
              customer.setCust_Num(fieldSet.readInt("id"));
              customer.setName_Surname(fieldSet.readString("name"));
          }catch (Exception e){
              log.error("Mapping error, " + e);
          }
          return customer;
       }
    }
    

    But your issue is not on reading side (and the FieldSetMapper), it is on the writer side. The JdbcBatchItemWriter uses a ItemSqlParameterSourceProvider to extract data from your Customer object and set them on the sql query. By default, a BeanPropertyItemSqlParameterSourceProvider is used, this implementation extracts data based on JavaBeans naming conventions. However, you can provide a custom implementation, which in your case would be something like:

    class CustomerItemSqlParameterSourceProvider implements ItemSqlParameterSourceProvider<Customer> {
    
        @Override
        public SqlParameterSource createSqlParameterSource(Customer customer) {
            Map<String, Object> params = new HashMap<>();
            params.put("custNum", customer.getCust_Num());
            params.put("nameSurname", customer.getName_Surname());
            return new MapSqlParameterSource(params);
        }
    }
    

    Then you set this CustomerItemSqlParameterSourceProvider on your writer.

    Hope this helps.