2

I have a requirement to read data from 3 tables (CONTRACT, LANE, RATE). where contract can be multiple and and for each CONTRACT I can have multiple LANE and For each LANE I can have multiple RATES. Tables has foreign key relation.

I want to read the data from table and set into Java Objects in related manner. Java POJO are like.

  public class Contract extends TICSection {

    private RTRate rtRate;

    private List<Lane> lanes = new ArrayList<Lane>();

    private String effectiveDate;
    private String expirationDate;
    private String contractNumber;
    private String contractTitle;
  }

Lane

public class Lane extends TICSection {  

private List<Rate> rate = new ArrayList<Rate>();
private String laneNumber ;

private String laneId ;
private String opFlag ;

private String depCntry ;
private String destCntry;
}   

Rate

public class Rate extends TICSection {

private String priceComponent ;
private String price ;

private String currency ;
private String quantityUnit ;

private String priceUnit ;
}   

In java object Contract I have list of Lanes and in Lane I have list of Rates. I want all the data from 3 corresponding tables in this format.

Please help me how can I set the data in this format using ItemReader.

Also if i set commit-interval=10 in my context xml how can I make sure spring batch reads and processes only 1 Contract and 10 Lane at a time. Is it possible ?

Problem is I am trying to read data from 3 different tables and I want to map them to Java objects in the similar way Like Contract having list of lanes associated to it and lane having list of rates associated to it. so that when I process the records I get all related lane and rates data.

My configuration looks like this for 1 table

<bean id="pagingItemReader"
    class="org.springframework.batch.item.database.JdbcPagingItemReader"
    scope="step">
    <property name="dataSource" ref="dataSource" />
    <property name="queryProvider">
        <bean
            class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="selectClause" value="*" />
            <property name="fromClause" value="from gtn_lineitem" />
            <property name="whereClause" value="record_status in ('O','E') and contract_seq = :contractSeq" />
            <property name="sortKey" value="contract_seq" />
        </bean>
    </property>
    <!-- Inject via the ExecutionContext in rangePartitioner -->
    <property name="parameterValues">
      <map>
        <entry key="contractSeq" value="#{stepExecutionContext[contractSeq]}"/>
        <entry key="fromId" value="#{stepExecutionContext[fromId]}" />
        <entry key="toId" value="#{stepExecutionContext[toId]}" />
      </map>
    </property>
    <property name="pageSize" value="#{stepExecutionContext[laneCount]}" />
    <property name="rowMapper">
        <bean class="com.cat.srr.gtn.dao.mapper.GTNContractRowMapper" />
    </property>
</bean>


<!-- Actual Job -->


<bean id="contractReadPartitioner" class="com.cat.srr.gtn.batch.partitioner.ContractReadPartitioner">
    <property name="contractDao" ref="contractDao"></property>
</bean>
<bean id="taskExecutor" class="org.springframework.core.task.SyncTaskExecutor" />
<bean id="contractProcessor" class="com.cat.srr.gtn.batch.ContractProcessor" scope="step">
    <property name="threadName" value="#{stepExecutionContext[name]}"></property>
</bean>


<batch:job id="partitionJob" xmlns="http://www.springframework.org/schema/batch">
    <batch:step id="masterStep">
        <batch:partition step="slaveStep" partitioner="contractReadPartitioner">
            <batch:handler grid-size="1" task-executor="taskExecutor"/>
        </batch:partition>
    </batch:step>
    <batch:listeners>
        <batch:listener ref="jobListener" />
    </batch:listeners>
</batch:job>

 <batch:step id="slaveStep">
        <batch:tasklet transaction-manager="transactionManager">
            <batch:chunk reader="pagingItemReader" writer="contractWriter"
                processor="contractProcessor" commit-interval="1" />
        </batch:tasklet>
 </batch:step>

But if declare 3 readers for 3 different table how to merge the data into java objects. Or If I write a join query to select all the related data then How the Row Mapper will merge the results. What should be the correct approach.

public class GTNContractRowMapper implements RowMapper<GTNContract>{

@Override
public GTNContract mapRow(ResultSet rs, int arg1) throws SQLException {
    GTNContract contract = new GTNContract();
    contract.setContractSeq(rs.getString("CONTRACT_SEQ"));
    contract.setContractNumber(rs.getString("GTN_CONTRACT_ID"));
    contract.setContractTitle(rs.getString("CONTRACT_ID"));
    contract.setCarrierName(rs.getString("CARRIER_NAME"));
    contract.setCarrierSCAC(rs.getString("CARRIER_SCAC"));
    contract.setModeName(rs.getString("MODE_NM"));
    contract.setEffectiveDate(rs.getString("TERM_BEGIN"));
    contract.setExpirationDate(rs.getString("TERM_END"));


    return contract;
}

}

Sushil
  • 364
  • 6
  • 22
  • OK, we have your classes, which is the first part of an MCVE: http://stackoverflow.com/help/mcve Now we need to see what you have done with them and what your output or compile errors are. – Daniel Wisehart Aug 31 '16 at 13:58
  • @DanielWisehart I have updated the question with more details – Sushil Aug 31 '16 at 14:15

1 Answers1

2

Actually I need information for your Processor and Writer as well. Base on that information, I can give appropriate solution.

But with Reader information you gave, I would like to go with the 2 below approaches

A. No Custom Reader 1. Partitioner will handle at Contract level. 2. Reader will read LANE information base on each contract. 3. Processor will get RATE information base on each contract and lane

So at Processor level, we have each Contract/Lane/Rate.

I'm waiting for more information from your processor and writer to provide accurate approach.

B. Custom Reader The custom one, you can build your own logic with a query which joins 3 tables with GROUP BY clause. And from Java, you loop on the result set and build your Contract objects.

Thanks, Nghia

Nghia Do
  • 2,588
  • 2
  • 17
  • 31
  • Thanks for the quick reply. My processor is like i have to process each lane record one by one and some bussiness logic and then in writer I want to call Webservice to push the data. If I go with No Custome Reader approach, How the contract and lane records will get merged in 1 object. Can you please give me some sample – Sushil Aug 31 '16 at 18:08
  • If you go with *No Custom Reader*, the processor will be as below public class JdbcPagingItemProcessor implements ItemProcessor { Autowire private LaneDAO laneDAO; Override public Lane process(Lane lane) throws Exception { // pull Rate details List rateList = this.laneDAO.fetchRatesByLaneId(lane.getLaneNumber()); land.setRateList(rateList); // start processing by Lane return } } Note: Lane class should have Contract field to refer to parent object. – Nghia Do Aug 31 '16 at 18:19
  • So you mean in this case i will have to find all the rates for lane that is ok but also contract associated to lane. ? – Sushil Aug 31 '16 at 18:38
  • Remember Partitioner is for Contract level already. When I come to Reader, it means we already 'read' per Contract. And then Processor is only for Lane belong to a specific Contract. – Nghia Do Aug 31 '16 at 18:47
  • Ok Thank you. I will try this option :) – Sushil Aug 31 '16 at 18:48
  • I tried the No custom reader option using Partitioner and processor to read Contract and Lane. Here I am getting all the Lanes for the Contract but problem is I have to read all the contract one by one if give grid-size=1 then only 1 contract is read and it comes out but does not go for other Contract. I cannot create multiple threads as my requirement is that it has to do one by one. How can I loop again the same partitioner for next contract ? – Sushil Sep 01 '16 at 13:17
  • If you use Partitioner with Grid-Size (such as 5) and add each of contract to ExecutionContext, Spring Batch will generate threads for Contract. You can take a look on the link https://bigzidane.wordpress.com/2016/08/30/stop-spring-batch-job-if-a-thread-of-the-processor-caught-an-exception/ to see how to build Partitioner. – Nghia Do Sep 01 '16 at 13:33
  • yes but I want to go with 1 thread only, my requirement is to run with 1 thread only because 2nd contract is dependent on 1st. So it has to be sequential. Is there any way to do it with 1 thread only – Sushil Sep 01 '16 at 13:49
  • I believe you are using SimpleAsyncTaskExecutor. You can use SyncTaskExecutor to achieve your requirements. – Nghia Do Sep 01 '16 at 14:03
  • i tried that SyncTaskExecutor as well but that is also creating multiple threads and executing 1 by 1. – Sushil Sep 01 '16 at 14:08
  • is there any way to loop partitioner ? – Sushil Sep 01 '16 at 14:13
  • SyncTask creates a main thread and use it in sequence. It is actually loop at partitioner level – Nghia Do Sep 01 '16 at 14:33
  • *(SyncTask creates a main thread and use it in sequence. It is actually loop at partitioner level)* If you see my context.xml I am using SyncTaskExecutor only, but still it is not looping, If I remove grid-size then by default it is creating 6 threads. Can you please let me know what I am missing – Sushil Sep 01 '16 at 15:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122439/discussion-between-nghia-do-and-sushil). – Nghia Do Sep 01 '16 at 16:07