1
votes

The spring Batch program which I am working on is reading data from a table. It’s using ‘org.springframework.batch.item.database.JdbcCursorItemReader’ itemReader . Earlier the plan was to Alter table and add a PROCESSED_INDICATOR flag and prepopulate it with status ‘PENDING’. Once the record is processed and writer will update the status of PROCESSED_INDICATOR flag to ‘Processed’. This is to support re-startability . For example if batch picks up 1 million records and died in ½ million records then when I restart the batch; it should start where I have left off. But unfortunately, management didn’t approve this solution. I am digging in ways to make itemreader re-startable. As per Spring documentation “Most ItemReaders have much more sophisticated restart logic. The JdbcCursorItemReader, for example, stores the row id of the last processed row in the Cursor.”

Does anyone have any sample example of such custom reader which implements JdbcCursorItemReader and stores last processed row in the cursor. https://docs.spring.io/spring-batch/trunk/reference/html/readersAndWriters.html

==FULL XML CONFIGURATION==

<import resource="classpath:/batch/utility/skip/batch_skip.xml" />
<import resource="classpath:/batch/config/context-postgres.xml" />
<import resource="classpath:/batch/config/oracle-database.xml" />

<context:property-placeholder
    location="classpath:/batch/jobs/TPF-1001-DD-01/TPF-1001-DD-01.properties" />
<bean id="gridSizePartitioner"
    class="com.tpf.partitioner.GridSizePartitioner" />

      <task:executor id="taskExecutor" pool-size="${pool.size}" />
<batch:job id="XYZJob" job-repository="jobRepository"
    restartable="true">

    <batch:step id="XYZSTEP">
        <batch:description>Convert TIF files to PDF</batch:description>
        <batch:partition partitioner="gridSizePartitioner">

            <batch:handler task-executor="taskExecutor"
                grid-size="${pool.size}" />
            <batch:step>
                <batch:tasklet allow-start-if-complete="true">
                    <batch:chunk commit-interval="${commit.interval}"
                        skip-limit="${job.skip.limit}">

                        <batch:reader>
                            <bean id="timeReader"
                                class="org.springframework.batch.item.database.JdbcCursorItemReader"
                                scope="step">
                                <property name="dataSource" ref="oracledataSource" />
                                <property name="sql">
                                    <value>                                     
                                    select TIME_ID as timesheetId,count(*),max(CREATION_DATETIME) as creationDateTime , ILN_NUMBER as ilnNumber
                                    from TS_FAKE_NAME
                                    where creation_datetime  >= '#{jobParameters['creation_start_date1']} 12.00.00.000000000 AM' 
                                    and creation_datetime &lt;  '#{jobParameters['creation_start_date2']} 11.59.59.999999999 PM' 
                                    and mod(time_id,${pool.size})=#{stepExecutionContext['partition.id']} 
                                    group by  time_id ,ILN_NUMBER                                   

                                    </value>
                                </property>
                                <property name="rowMapper">
                                    <bean
                                        class="org.springframework.jdbc.core.BeanPropertyRowMapper">
                                        <property name="mappedClass"
                                            value="com.tpf.model.Time" />
                                    </bean>
                                </property>
                            </bean>
                        </batch:reader>
                        <batch:processor>
                            <bean id="compositeItemProcessor"
                                class="org.springframework.batch.item.support.CompositeItemProcessor">
                                <property name="delegates">
                                    <list>
                                        <ref bean="timeProcessor" />
                                    </list>
                                </property>

                            </bean>
                        </batch:processor>


                        <batch:writer>
                            <bean id="compositeItemWriter"
                                class="org.springframework.batch.item.support.CompositeItemWriter">
                                <property name="delegates">
                                    <list>
                                        <ref bean="timeWriter" />
                                    </list>
                                </property>
                            </bean>
                        </batch:writer>
                        <batch:skippable-exception-classes>
                            <batch:include
                                class="com.utility.skip.BatchSkipException" />
                        </batch:skippable-exception-classes>
                        <batch:listeners>
                            <batch:listener ref="batchSkipListener" />
                        </batch:listeners>
                    </batch:chunk>
                </batch:tasklet>
            </batch:step>
        </batch:partition>
    </batch:step>
    <batch:validator>
        <bean
            class="org.springframework.batch.core.job.DefaultJobParametersValidator">
            <property name="requiredKeys">
                <list>
                    <value>batchRunNumber</value>
                    <value>creation_start_date1</value>
                    <value>creation_start_date2</value>
                </list>
            </property>
        </bean>
    </batch:validator>
</batch:job>



<bean id="timesheetWriter" class="com.tpf.writer.TimeWriter"
    scope="step">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="timeProcessor"
    class="com.tpf.processor.TimeProcessor" scope="step">
    <property name="dataSource" ref="oracledataSource" />
</bean> 

1

1 Answers

0
votes

Does anyone have any sample example of such custom reader which implements JdbcCursorItemReader and stores last processed row in the cursor

The JdbcCursorItemReader does that, see Javadoc, here is an excerpt:

ExecutionContext: The current row is returned as restart data,
and when restored from that same data, the cursor is opened and the current row
set to the value within the restart data.

So you don't need a custom reader.