TERASOLUNA Batch Framework for Java (5.x) Development Guideline - version 5.1.1.RELEASE, 2018-3-16
> INDEX

Overview

MyBatis3 (hereafter, called [MyBatis]) is used for database access in TERASOLUNA Batch 5.x. Please refer below TERASOLUNA Server 5.x Development Guideline for basic usage of database access using MyBatis.

This chapter focuses on the usage specific to TERASOLUNA Batch 5.x.

Notes for how to use Oracle JDBC in Linux environment

While using Oracle JDBC in Linux environment, locking of random generator number of OS used by Oracle JDBC occurs. Hence, even though jobs are attempted to be executed in parallel, events for sequential execution and events for one connection timeout occur.
2 patterns for how to avoid these events are shown below.

  • Set following in system properties while executing Java command.

    • -Djava.security.egd=file:///dev/urandom

  • Change securerandom.source=/dev/random in ${JAVA_HOME}/jre/lib/security/java.security to securerandom.source=/dev/urandom.

How to use

Explain how to use database access as TERASOLUNA Batch 5.x.

It must be remembered that how to access database varies for chunk model and tasklet model.

There are following 2 ways to use database access in TERASOLUNA Batch 5.x.
Please select the method based on the components accessing the database.

  1. Use ItemReader and ItemWriter for MyBatis.

    • For Input/Output by using database access as chunk model.

      • org.mybatis.spring.batch.MyBatisCursorItemReader

      • org.mybatis.spring.batch.MyBatisBatchItemWriter

  2. Use Mapper interface

    • Used for business logic processing in chunk model.

      • With ItemProcessor implementation.

    • For whole database access as tasklet model.

      • With Tasklet implementation.

Common Settings

Explain common settings required for database access.

DataSource Setting

It assumes two data sources in TERASOLUNA Batch 5.x. Show 2 default data sources in launch-context.xml.

Data source list
Data source name Description

adminDataSource

Data source used by Spring Batch and TERASOLUNA Batch 5.x
It is used in JobRepository and Asynchronous execution(DB polling)

jobDataSource

Data source used by job

Show the property of connection information and launch-context.xml below.

Set these settings according to the user’s environment.

resources\META-INF\spring\launch-context.xml
<!-- (1) -->
<bean id="adminDataSource" class="org.apache.commons.dbcp2.BasicDataSource"
      destroy-method="close"
      p:driverClassName="${admin.h2.jdbc.driver}"
      p:url="${admin.h2.jdbc.url}"
      p:username="${admin.h2.jdbc.username}"
      p:password="${admin.h2.jdbc.password}"
      p:maxTotal="10"
      p:minIdle="1"
      p:maxWaitMillis="5000"
      p:defaultAutoCommit="false"/>

<!-- (2) -->
<bean id="jobDataSource" class="org.apache.commons.dbcp2.BasicDataSource" 
      destroy-method="close"
      p:driverClassName="${jdbc.driver}"
      p:url="${jdbc.url}"
      p:username="${jdbc.username}"
      p:password="${jdbc.password}"
      p:maxTotal="10"
      p:minIdle="1"
      p:maxWaitMillis="5000"
      p:defaultAutoCommit="false" />
batch-application.properties
# (3)
# Admin DataSource settings.
admin.jdbc.driver=org.h2.Driver
admin.jdbc.url=jdbc:h2:mem:batch;DB_CLOSE_DELAY=-1
admin.jdbc.username=sa
admin.jdbc.password=

# (4)
# Job DataSource settings.
jdbc.driver=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost:5432/postgres
jdbc.username=postgres
jdbc.password=postgres
Description
Sr. No. Description

(1)

adminDataSource definition. Connection information of (3) is set.

(2)

jobDataSource definition. Connection information of (4) is set.

(3)

Connection information to the database used by adminDataSource
H2 is used in this example.

(4)

Connection information to the database used by jobDataSource
PostgreSQL is used in this example.

MyBatis Setting

Important points for setting MyBatis on TERASOLUNA Batch 5.x.

One of the important points in implementing batch processing is "to efficiently process large amounts of data with certain resources"
Explain the setting.

  • fetchSize

    • In general batch processing, it is mandatory to specify the appropriate fetchSize for the JDBC driver to reduce the communication cost of processing large amounts of data. fetchSize is a parameter that sets the number of data to be acquired by one communication between the JDBC driver and the database. It is desirable to set this value as large as possible. However, if it is too large, it presses memory. So please be careful. user has to tune the parameter.

    • In MyBatis, user can set defaultFetchSize as a common setting for all queries, and can override it with fetchSize setting for each query.

  • executorType

    • In general batch processing, the same SQL is executed within the same transaction for the number of total data count/fetchSize. At this time, it is possible to process efficiently by reusing a statement instead of creating it each time.

    • In the MyBatis setting, it can reuse statements by setting REUSE in defaultExecutorType and contributes to improved processing throughput.

    • When updating a large amount of data at once, performance improvement can be expected by using batch update of JDBC.
      Therefore, SqlSessionTemplate used in MyBatisBatchItemWriter
      is set to BATCH (not REUSE) in executorType.

In TERASOLUNA Batch 5.x, two different ExecutorType exists at the same time. It is assumed that it is often implemented by one ExecutorType, but special attention is required when using them together. The detail will be explained in [Ch05_DBAccess_HowToUse_Input_MapperInterface].

Other parameters of MyBatis

For other parameters, refer to the following links and make settings that match the application characteristics.
http://www.mybatis.org/mybatis-3/configuration.html

Show the default setting below.

META-INF/spring/launch-context.xml
<bean id="jobSqlSessionFactory"
      class="org.mybatis.spring.SqlSessionFactoryBean"
      p:dataSource-ref="jobDataSource">
    <!-- (1) -->
    <property name="configuration">
        <bean class="org.apache.ibatis.session.Configuration"
              p:localCacheScope="STATEMENT"
              p:lazyLoadingEnabled="true"
              p:aggressiveLazyLoading="false"
              p:defaultFetchSize="1000"
              p:defaultExecutorType="REUSE"/>
    </property>
</bean>

<!-- (2) -->
<bean id="batchModeSqlSessionTemplate"
      class="org.mybatis.spring.SqlSessionTemplate"
      c:sqlSessionFactory-ref="jobSqlSessionFactory"
      c:executorType="BATCH"/>
Description
Sr. No. Description

(1)

Various settings of MyBatis
fetchSize is set to 1000 by default.

(2)

For MyBatisBatchItemWriter, executorType defines SqlSessionTemplate of BATCH.

For the definition of SqlSessionFactory using adminDataSource

When performing synchronous execution, SqlSessionFactory using adminDataSource is unnecessary and is not defined. When performing Asynchronous execution(DB polling), it is defined in META-INF/spring/async-batch-daemon.xml to access the Job-request-table.

META-INF/spring/async-batch-daemon.xml
<bean id="adminSqlSessionFactory"
      class="org.mybatis.spring.SqlSessionFactoryBean"
      p:dataSource-ref="adminDataSource" >
    <property name="configuration">
        <bean class="org.apache.ibatis.session.Configuration"
              p:localCacheScope="STATEMENT"
              p:lazyLoadingEnabled="true"
              p:aggressiveLazyLoading="false"
              p:defaultFetchSize="1000"
              p:defaultExecutorType="REUSE"/>
    </property>
</bean>

Mapper XML definition

Since there is no specific explanation for TERASOLUNA Batch 5.x, please refer to the Implementation of database access process in TERASOLUNA Server 5.x Development Guideline.

MyBatis-Spring setting

When using ItemReader and ItemWriter provided by MyBatis-Spring, it is necessary to set Mapper XML used in Mapper’s Config.

Following two methods are given as setting methods.

  1. Register Mapper XML to be used for all jobs as a common setting.

    • All Mapper XML has to be described in META-INF/spring/launch-context.xml.

  2. Register Mapper XML to be used for each job as individual setting.

    • Mapper XML required by each job has to be described in bean definition under META-INF/jobs/

If common settings are made, not only Mapper XML of jobs executed when executing synchronous execution but also Mapper XML used by other jobs are read. As a result of this, the following adverse effects occur.

  • It takes time to start the job

  • Consumption of memory resources increases

To avoid it, TERASOLUNA Batch 5.x adopts a setting method that specifies only Mapper XML that the job requires for each job definition as individual setting.

For the basic setting method, please refer to MyBatis-Spring settings in TERASOLUNA Server 5.x Development Guideline.

In TERASOLUNA Batch 5.x, since multiple SqlSessionFactory and SqlSessionTemplate are defined, it is necessary to explicitly specify which one to use.
Basically, specify jobSqlSessionFactory

Show setting example below.

META-INF/jobs/common/jobCustomerList01.xml
<!-- (1) -->
<mybatis:scan
    base-package="org.terasoluna.batch.functionaltest.app.repository.mst"
    factory-ref="jobSqlSessionFactory"/>
Description
Sr. No. Description

(1)

Set jobSqlSessionFactory in factory-ref attribute of <mybatis:scan>

Input

Input of database access is explained as follows.

MyBatisCursorItemReader

Here, database access by MyBatisCursorItemReader provided by MyBatis-Spring as ItemReader is explained.

Functional overview

MyBatis-Spring provides the following two ItemReader.

  • org.mybatis.spring.batch.MyBatisCursorItemReader

  • org.mybatis.spring.batch.MyBatisPagingItemReader

MyBatisPagingItemReader is an ItemReader that uses the mechanism described in Pagination search for Entity (SQL refinement method) of TERASOLUNA Server 5.x Development Guideline.
Since SQL is issued again after acquiring a certain number of cases, there is a possibility that data consistency may not be maintained. Therefore, it is dangerous to use it in batch processing, so TERASOLUNA Batch 5.x does not use it in principle.
TERASOLUNA Batch 5.x uses only MyBatisCursorItemReader that uses Cursor and returns fetch data by linking with MyBatis.

In TERASOLUNA Batch 5.x, as explained in MyBatis-Spring setting, a method to dynamically register Mapper XML with mybatis:scan is adopted. Therefore, it is necessary to prepare an interface corresponding to Mapper XML. For details, please refer to Implementation of database access process in TERASOLUNA Server 5.x Development Guideline.

Notes on closing in MyBatisCursorItemReader

java.lang.NullPointerException occurs when MyBatisCursorItemReader is closed without opening it (abnormal termination by @BeforeStep annotation) due to problem occurred in Mybatis-Spring1.3.1. In that case, it is necessary to extend MyBatisCursorItemReader, catch the exception at the time of closing and implement such that it terminates normally.

Implementation example for referring database by using MyBatisCursorItemReader is explained below for each process model.

How to use in chunk model

Implementation example for referring database using MyBatisCursorItemReader in chunk model is shown below.
Here, implementation example of
MyBatisCursorItemReader and implementation example of ItemProcessor for processing the data fetched from database using the implemented MyBatisCursorItemReader`
are explained.

Bean definition
<!-- (1) -->
<mybatis:scan
    base-package="org.terasoluna.batch.functionaltest.app.repository.mst"
    factory-ref="jobSqlSessionFactory"/>

<!-- (2) (3) (4) -->
<bean id="reader" class="org.mybatis.spring.batch.MyBatisCursorItemReader"
      class="org.mybatis.spring.batch.MyBatisCursorItemReader" scope="step"
      p:queryId="org.terasoluna.batch.functionaltest.app.repository.mst.CustomerRepository.findAll"
      p:sqlSessionFactory-ref="jobSqlSessionFactory"/>
<batch:job id="outputAllCustomerList01" job-repository="jobRepository">
    <batch:step id="outputAllCustomerList01.step01">
        <batch:tasklet transaction-manager="jobTransactionManager">
            <batch:chunk reader="reader"
                         processor="retrieveBranchFromContextItemProcessor"
                         writer="writer" commit-interval="10"/>
            <!-- omitted -->
        </batch:tasklet>
    </batch:step>
</batch:job>
Mapper XML
<!-- (6) -->
<mapper namespace="org.terasoluna.batch.functionaltest.app.repository.mst.CustomerRepository">

    <!-- omitted -->

    <!-- (7) -->
    <select id="findAll" resultType="org.terasoluna.batch.functionaltest.app.model.mst.Customer">
        <![CDATA[
        SELECT
            customer_id AS customerId,
            customer_name AS customerName,
            customer_address AS customerAddress,
            customer_tel AS customerTel,
            charge_branch_id AS chargeBranchId,
            create_date AS createDate,
            update_date AS updateDate
        FROM
            customer_mst
        ORDER by
            charge_branch_id ASC, customer_id ASC
        ]]>
    </select>

    <!-- omitted -->
</mapper>
Mapper interface
public interface CustomerRepository {
    // (8)
    List<Customer> findAll();

    // omitted.
}
ItemProcessor implementation
@Component
@Scope("step")
public class RetrieveBranchFromContextItemProcessor implements ItemProcessor<Customer, CustomerWithBranch> {
    // omitted.
     @Override
    public CustomerWithBranch process(Customer item) throws Exception { // (9)
        CustomerWithBranch newItem = new CustomerWithBranch(item);
        newItem.setBranch(branches.get(item.getChargeBranchId())); // (10)
        return newItem;
    }
}
Description
Sr. No. Description

(1)

Register Mapper XML.

(2)

Define MyBatisCursorItemReader.

(3)

Specify the SQL ID defined in (7) with namespace + <method name> of (6) to the property of queryId.

(4)

Specify SqlSessionFactory of the database to be accessed in sqlSessionFactory-ref property.

(5)

Specify MyBatisCursorItemReader defined in (2) in reader attribute.

(6)

Define Mapper XML. Match the value of namespace with the FQCN of the interface.

(7)

Define SQL.

(8)

Define the method corresponding to the SQL ID defined in (7) for the interface.
In this example, branchId is passed as parameter of search condition by @Param annotation. It is not required when there is no condition.

(9)

The type of item received as an argument is SalesPerformanceDetail that is input object type specified in type argument of ItemProcessor interface implemented in this class.

How to use in tasklet model

Implementation example for referring database using MyBatisCursorItemReader in tasklet model is shown below.
Here, implementation example of
MyBatisCursorItemReader and implementation example of Tasklet for processing the data fetched from database using the implemented MyBatisCursorItemReader`
are explained.

For the points to keep in mind while using component of chunk model in tasklet model, refer to Tasklet implementation that uses component of Chunk model.

Unlike chunk model, in tasklet model resources should be explicitly opened/closed for Tasklet implementation. Input data is also read explicitly.

Bean definition
<!-- (1) -->
<mybatis:scan
    base-package="org.terasoluna.batch.functionaltest.app.repository.plan"
    factory-ref="jobSqlSessionFactory"/>

<!-- (2) (3) (4) -->
<bean id="summarizeDetails" class="org.mybatis.spring.batch.MyBatisCursorItemReader"
      p:queryId="org.terasoluna.batch.functionaltest.app.repository.plan.SalesPlanDetailRepository.summarizeDetails"
      p:sqlSessionFactory-ref="jobSqlSessionFactory"/>

<batch:job id="customizedJobExitCodeTaskletJob" job-repository="jobRepository">
    <batch:step id="customizedJobExitCodeTaskletJob.step01">
        <batch:tasklet transaction-manager="jobTransactionManager" ref="checkAmountTasklet"/>
    </batch:step>
    <!-- omitted -->
</batch:job>
Mapper XML
<!-- (5) -->
<mapper namespace="org.terasoluna.batch.functionaltest.app.repository.plan.SalesPlanDetailRepository">

    <!-- omitted -->

    <!-- (6) -->
    <select id="summarizeDetails" resultType="org.terasoluna.batch.functionaltest.app.model.plan.SalesPlanSummary">
     SELECT
        branch_id AS branchId, year, month, SUM(amount) AS amount
     FROM
        sales_plan_detail
     GROUP BY
        branch_id, year, month
     ORDER BY
        branch_id ASC, year ASC, month ASC
     ]]>
    </select>

</mapper>
Mapper interface
public interface SalesPlanDetailRepository {

    // (7)
    List<SalesPlanSummary> summarizeDetails();

        // omitted.
}
Tasklelet implementation
@Component
@Scope("step")
public class CheckAmountTasklet implements Tasklet {
    // (8)
    @Inject
    ItemStreamReader<SalesPlanSummary> reader;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        SalesPlanSummary item = null;
         List<SalesPlanSummary> items = new ArrayList<>(CHUNK_SIZE);
        int errorCount = 0;
         try {
            // (9)
            reader.open(chunkContext.getStepContext().getStepExecution().getExecutionContext());
            while ((item = reader.read()) != null) { // (10)
                if (item.getAmount().signum() == -1) {
                    logger.warn("amount is negative. skip item [item: {}]", item);
                    errorCount++;
                    continue;
                }
                 // omitted.
            }

            // catch block is omitted.
        } finally {
            // (11)
            reader.close();
        }
    }
    // omitted.

    return RepeatStatus.FINISHED;
}
Description
Sr. No. Description

(1)

Register Mapper XML.

(2)

Define MyBatisCursorItemReader.

(3)

Specify the SQL ID defined in (6) with namespace + <method name> of (5) to the property of queryId.

(4)

Specify SqlSessionFactory of the database to be accessed in sqlSessionTemplate-ref property.

(5)

Define Mapper XML. Match the value of namespace with the FQCN of the interface.

(6)

Define SQL.

(7)

Define the method corresponding to the SQL ID defined in (6) for the interface.

(8)

Assign @Inject annotation and inject the implementation of ItemStreamReader.
Since it is necessary to open/close the target resource, inject implementation in ItemStreamReader interface having resource open/close method in ItemReader.

(9)

Open input resource.

(10)

Read input data one by one.

(11)

Close input resource.
Resource should always be closed. Here, when an exception occurs, the transaction of the entire tasklet is rolled back, stack trace of the exception is output and the job ends abnormally. Therefore, exception handling should be implemented whenever required.

How to specify search condition

When you want to search by specifying search condition while accessing database, search conditions can be specified by fetching values from job parameters in Map format in Bean definition and setting key. An example of job start command that specifies job parameters and an implementation example are shown below.

Job start command when job parameters are specified.
java -cp ${CLASSPATH} org.springframework.batch.core.launch.support.CommandLineJobRunner
 /META-INF/job/job001 job001 year=2017 month=12
Implementation example of MapperXML
<!-- (1) -->
<select id="findByYearAndMonth"
    resultType="org.terasoluna.batch.functionaltest.app.model.performance.SalesPerformanceSummary">
    <![CDATA[
    SELECT
        branch_id AS branchId, year, month, amount
    FROM
        sales_performance_summary
    WHERE
        year = #{year} AND month = #{month}
    ORDER BY
        branch_id ASC
    ]]>
</select>

<!-- omitted -->
Bean definition
<!-- omitted -->

<!-- (2) -->
<bean id="reader"
      class="org.mybatis.spring.batch.MyBatisCursorItemReader" scope="step"
      p:queryId="org.terasoluna.batch.functionaltest.ch08.parallelandmultiple.repository.SalesSummaryRepository.findByYearAndMonth"
      p:sqlSessionFactory-ref="jobSqlSessionFactory">
    <property name="parameterValues"> <!-- (3) -->
        <map>
            <!-- (4) -->
            <entry key="year" value="#{jobParameters['year']}" value-type="java.lang.Integer"/>
            <entry key="month" value="#{jobParameters['month']}" value-type="java.lang.Integer"/>

            <!-- omitted -->
        </map>
    </property>
</bean>

<!-- omitted -->
Description
Sr. No. Description

(1)

Specify search condition and define the SQL to be fetched.

(2)

Define ItemReader to fetch data from database.

(3)

Set parameterValues in property name.

(4)

Specify search conditions by fetching values to be set in search condition from job parameters and by setting as key. Since SQL arguments are defined in numerical value, they are passed by converting to Integer by value-type.

How to specify search by StepExectionContext

When search condition is to be specified in pre-process of job such as @beforeStep, the values can be fetched same as JobParameters by setting to StepExecutionContext.

Mapper interface (Input)

Use Mapper interface for referring database in other than ItemReader.
Here, the reference of database using Mapper interface is explained.

Functional overview

Following restrictions are provided in TERASOLUNA Batch 5.x for using Mapper interface.

The available points of Mapper interface.
Process ItemProcessor Tasklet Listener

Reference

Available

Available

Available

Update

Conditionally available

Available

Unavailable

Restrictions in ItemProcessor

There is a restriction that it should not be executed with two or more ExecutorType within the same transaction in MyBatis.
If "use MyBatisBatchItemWriter for ItemWriter" and "use ItemProcessor to update and reference the Mapper interface" are satisfied at the same time, it conflicts with this restriction.
To avoid this restriction, database is accessed by using Mapper interface that ExecutorType is BATCH in ItemProcessor.
In addition, MyBatisBatchItemWriter checks whether it is SQL issued by itself with the status check after executing SQL but naturally it can not manage SQL execution by ItemProcessor and an error will occur.
Therefore, if MyBatisBatchItemWriter is used, updating with the Mapper interface will not be possible and only reference.

It can set to invalidate the error check of MyBatisBatchItemWriter, but the setting is prohibited because there is a possibility that unexpected behavior may occur.

Restrictions in Tasklet

In Tasklet, since it is basic to use the Mapper interface, there is no influence like ItemProcessor.
It is possible to use MyBatisBatchItemWriter by Inject, but in that case Mapper interface itself can be processed with BATCH setting. In other words, there is basically no need to use MyBatisBatchItemWriter by Inject.

How to use in chunk model

Implementation example for referring the database using Mapper interface in chunk model is shown below.

Implementation example with ItemProcessor
@Component
public class UpdateItemFromDBProcessor implements
        ItemProcessor<SalesPerformanceDetail, SalesPlanDetail> {

    // (1)
    @Inject
    CustomerRepository customerRepository;

    @Override
    public SalesPlanDetail process(SalesPerformanceDetail readItem) throws Exception {

        // (2)
        Customer customer = customerRepository.findOne(readItem.getCustomerId());

       // omitted.

        return writeItem;
    }
}
Bean definition
<!-- (3) -->
<mybatis:scan
        base-package="org.terasoluna.batch.functionaltest.app.repository"
        template-ref="batchModeSqlSessionTemplate"/>

<!-- (4) -->
<bean id="reader" class="org.mybatis.spring.batch.MyBatisCursorItemReader"
      p:queryId="org.terasoluna.batch.functionaltest.app.repository.performance.SalesPerformanceDetailRepository.findAll"
      p:sqlSessionFactory-ref="jobSqlSessionFactory"/>

<!-- omitted job definition -->

The contents for Mapper interface and Mapper XML are omitted as they are not different than the contents explained in MyBatisCursorItemReader.

Description

Sr. No.

Description

(1)

Inject Mapper interface.

(2)

Perform search process in Mapper interface.

(3)

Register Mapper XML.
By specifying batchModeSqlSessionTemplate set as BATCH in template-ref attribute, database access with ItemProcessor is BATCH.

(4)

Define MyBatisCursorItemReader.
Specify SqlSessionFactory of the database to be accessed in sqlSessionFactory-ref property.

Supplement of MyBatisCursorItemReader setting

Different ExecutorType can be used for MyBatisCursorItemReader and MyBatisBatchItemWriter like the definition example below. This is because the transaction by MyBatisCursorItemReader is different from the transaction of ItemWriter.

<bean id="reader" class="org.mybatis.spring.batch.MyBatisCursorItemReader"
      p:queryId="xxx"
      p:sqlSessionFactory-ref="jobSqlSessionFactory"/>

<bean id="writer" class="org.mybatis.spring.batch.MyBatisBatchItemWriter"
      p:statementId="yyy"
      p:sqlSessionTemplate-ref="batchModeSqlSessionTemplate"/>

How to use in tasklet model::

Implementation example for referring the database using Mapper interface in tasklet model is shown below..

Implementation example with Tasklet
@Component
public class OptimisticLockTasklet implements Tasklet {

    // (1)
    @Inject
    ExclusiveControlRepository repository;

    // omitted.

    @Override
    public RepeatStatus execute(StepContribution contribution,
            ChunkContext chunkContext) throws Exception {

        Branch branch = repository.branchFindOne(branchId); // (2)
        ExclusiveBranch exclusiveBranch = new ExclusiveBranch();

        // omitted.

        return RepeatStatus.FINISHED;
    }
}
Bean definition
<!-- (3) -->
<mybatis:scan
        base-package="org.terasoluna.batch.functionaltest.ch05.exclusivecontrol.repository"
        factory-ref="jobSqlSessionFactory"/>

<batch:job id="taskletOptimisticLockCheckJob" job-repository="jobRepository">
    <batch:step id="taskletOptimisticLockCheckJob.step01">
        <batch:tasklet transaction-manager="jobTransactionManager"
                       ref="optimisticLockTasklet"> <!-- (4) -->
        </batch:tasklet>
    </batch:step>
</batch:job>

The contents for Mapper interface and Mapper XML are omitted as they are not different than the contents explained in MyBatisCursorItemReader.

Description
Sr. No. Description

(1)

Inject Mapper interface.

(2)

Execute the search process with the Mapper interface.

(3)

Register Mapper XML.
Specify jobSqlSessionFactory set as REUSE in factory-ref attribute.

(4)

Inject Mapper interface and set Tasklet.

Output

The output of database access is explained as follows.

MyBatisBatchItemWriter

Here, database access by MyBatisBatchItemWriter provided by MyBatis-Spring as ItemWriter is explained.

Functional overview

MyBatis-Spring provides only one ItemWriter as shown below.

  • org.mybatis.spring.batch.MyBatisBatchItemWriter

MyBatisBatchItemWriteris an ItemWriter that uses batch update function of JDBC by linking with MyBatis and performance is expected to be improved when updating large amount of data at a time.
Basic configuration is same as MyBatisCursorItemReader. In MyBatisBatchItemWriter, batchModeSqlSessionTemplate described in MyBatis Setting should be specified.

Implementation example for updating database using MyBatisBatchItemWriter is shown below.

How to use in chunk model

Implementation example for updating (registering) database using MyBatisBatchItemWriter in chunk model is shown below.
Here, implementation example of MyBatisBatchItemWriter and implementation example of ItemProcessor that uses the implemented MyBatisBatchItemWriter are explained. The data fetched in ItemProcessor implementation is updated in database using MyBatisBatchItemWriter.

Bean definition
<!-- (1) -->
<mybatis:scan
    base-package="org.terasoluna.batch.functionaltest.ch05.exclusivecontrol.repository"
    factory-ref="jobSqlSessionFactory"/>

<!-- (2) (3) (4) -->
<bean id="writer"
      class="org.mybatis.spring.batch.MyBatisBatchItemWriter" scope="step"
      p:statementId="org.terasoluna.batch.functionaltest.ch05.exclusivecontrol.repository.ExclusiveControlRepository.branchExclusiveUpdate"
      p:sqlSessionTemplate-ref="batchModeSqlSessionTemplate"
      p:assertUpdates="#{new Boolean(jobParameters['assertUpdates'])}"/>

<batch:job id="chunkOptimisticLockCheckJob" job-repository="jobRepository">
    <batch:step id="chunkOptimisticLockCheckJob.step01">
        <batch:tasklet transaction-manager="jobTransactionManager">
            <batch:chunk reader="reader" processor="branchEditItemProcessor"
                         writer="writer" commit-interval="10"/> <!-- (5) -->
        </batch:tasklet>
    </batch:step>
</batch:job>
Mapper XML
<!-- (6) -->
<mapper namespace="org.terasoluna.batch.functionaltest.app.repository.plan.SalesPlanDetailRepository">

    <!-- (7) -->
    <insert id="create"
            parameterType="org.terasoluna.batch.functionaltest.app.model.plan.SalesPlanDetail">
        <![CDATA[
        INSERT INTO
            sales_plan_detail(branch_id, year, month, customer_id, amount)
        VALUES (
            #{branchId}, #{year}, #{month}, #{customerId}, #{amount}
        )
        ]]>
    </insert>

    <!-- omitted -->
</mapper>
Mapper interface
public interface SalesPlanDetailRepository {

    // (8)
    void create(SalesPlanDetail salesPlanDetail);

    // omitted.
}
ItemProcessor implementation
@Component
@Scope("step")
public class BranchEditItemProcessor implements ItemProcessor<Branch, ExclusiveBranch> {
    // omitted.

    @Override
    public ExclusiveBranch process(Branch item) throws Exception { // (9)
        ExclusiveBranch branch = new ExclusiveBranch();
        branch.setBranchId(item.getBranchId());
        branch.setBranchName(item.getBranchName() + " - " + identifier);
        branch.setBranchAddress(item.getBranchAddress() + " - " + identifier);
        branch.setBranchTel(item.getBranchTel());
        branch.setCreateDate(item.getUpdateDate());
        branch.setUpdateDate(new Timestamp(clock.millis()));
        branch.setOldBranchName(item.getBranchName());

        // (10)
        return branch;
    }
}
Description
Sr. No. Description

(1)

Register Mapper XML.

(2)

Define MyBatisBatchItemWriter.

(3)

Specify the SQL ID defined in (7) with namespace + <method name> of (6) to the property of statementId.

(4)

Specify SessionTemplate of the database to be accessed in sqlSessionTemplate-ref property.
For SessionTemplate to be specified, it is mandatory to set executorType to BATCH.

(5)

Specify MyBatisBatchItemWriter defined in (2) in writer attribute.

(6)

Define Mapper XML. Match the value of namespace with the FQCN of the interface.

(7)

Define SQL.

(8)

Define the method corresponding to the SQL ID defined in (7) in interface.

(9)

The return value type is ExclusiveBranch that is output object specified in ItemProcessor interface type argument implemented in this class.

(10)

By returning DTO object that sets update data, output the data to database.

How to use in tasklet model

Implementation example for updating (registering) database using MyBatisBatchItemWriter in tasklet model is shown below.
Here, implementation example of MyBatisBatchItemWriter and implementation example of Tasklet using the implemented MyBatisBatchItemWriter are explained. For the points to keep in mind while using component of chunk model in tasklet model, refer to Tasklet implementation that uses component of Chunk model.

Bean definition
<!-- (1) -->
<mybatis:scan base-package="org.terasoluna.batch.functionaltest.app.repository.plan"
            factory-ref="jobSqlSessionFactory"/>

<!-- (2) (3) (4) -->
<bean id="writer" class="org.mybatis.spring.batch.MyBatisBatchItemWriter"
          p:statementId="org.terasoluna.batch.functionaltest.app.repository.plan.SalesPlanDetailRepository.create"
          p:sqlSessionTemplate-ref="batchModeSqlSessionTemplate"/>

<batch:job id="taskletJobWithListenerWithinJobScope" job-repository="jobRepository">
    <batch:step id="taskletJobWithListenerWithinJobScope.step01">
        <batch:tasklet transaction-manager="jobTransactionManager" ref="salesPlanDetailRegisterTasklet"/>
    </batch:step>
    <!-- omitted. -->
</batch:job>
Mapper XML
<!-- (5) -->
<mapper namespace="org.terasoluna.batch.functionaltest.app.repository.plan.SalesPlanDetailRepository">

    <!-- (6) -->
    <insert id="create" parameterType="org.terasoluna.batch.functionaltest.app.model.plan.SalesPlanDetail">
        <![CDATA[
        INSERT INTO
            sales_plan_detail(branch_id, year, month, customer_id, amount)
        VALUES (
            #{branchId}, #{year}, #{month}, #{customerId}, #{amount}
        )
        ]]>
    </insert>

    <!-- omitted -->
</mapper>
Mapper interface
public interface SalesPlanDetailRepository {
    // (7)
    void create(SalesPlanDetail salesPlanDetail);

    // omitted.
}
Tasklet implementation
@Component
@Scope("step")
public class SalesPlanDetailRegisterTasklet implements Tasklet {

    // omitted.

    // (8)
    @Inject
    ItemWriter<SalesPlanDetail> writer;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        SalesPlanDetail item = null;

        try {
            reader.open(chunkContext.getStepContext().getStepExecution().getExecutionContext());

            List<SalesPlanDetail> items = new ArrayList<>(); // (9)

            while ((item = reader.read()) != null) {

                items.add(processor.process(item)); // (10)
                if (items.size() == 10) {
                    writer.write(items); // (11)
                    items.clear();
                }
            }
            // omitted.
        }
        // omitted.

        return RepeatStatus.FINISHED;
    }
}

The contents for Mapper interface and Mapper XML are omitted as they are not different than the contents explained in MyBatisBatchItemWriter.

Description
Sr. No. Description

(1)

Register Mapper XML.

(2)

Define MyBatisBatchItemWriter.

(3)

Specify the SQL ID defined in (6) with namespace + <method name> of (5) to the property of statementId.

(4)

Specify SessionTemplate of the database to be accessed in sqlSessionTemplate-ref property.
It is mandatory to set executorType to BATCH for SessionTemplate to be specified.

(5)

Define Mapper XML. Match the value of namespace with the FQCN of the interface.

(6)

Define SQL.

(7)

Define the method corresponding to the SQL ID defined in (6) for the interface.

(8)

Assign @Inject annotation and inject ItemWriter implementation.
Unlike ItemReader, open/close of resource is not required for updating database so inject in ItemWriter interface and not ItemStreamWriter.

(9)

Define list that stores output data.
ItemWriter outputs fixed number of data collectively.

(10)

Set update data in list.

(11)

Specify the list wherein update data is set, as an argument and output to database.

Mapper interface (Output)

Use Mapper interface for updating the database except for ItemWriter.
Here, database update using Mapper interface is described.

Functional overview

For the restrictions on TERASOLUNA Batch 5.x after database is accessed by using Mapper interface, refer to [Ch05_DBAccess_HowToUse_Input_MapperInterface].

How to use in chunk model

Implementation example for updating (registering) database using Mapper interface in chunk model is shown below.

Implementation example with ItemProcessor
@Component
public class UpdateCustomerItemProcessor implements ItemProcessor<Customer, Customer> {

    // omitted.

    // (1)
    @Inject
    DBAccessCustomerRepository customerRepository;

    @Override
    public Customer process(Customer item) throws Exception {
        item.setCustomerName(String.format("%s updated by mapper if", item.getCustomerName()));
        item.setCustomerAddress(String.format("%s updated by item writer", item.getCustomerAddress()));
        item.setUpdateDate(new Timestamp(clock.millis()));

        // (2)
        long cnt = customerRepository.updateName(item);

        // omitted.

        return item;
    }
}
Bean definition
<!-- (3) -->
<mybatis:scan
        base-package="org.terasoluna.batch.functionaltest.ch05.dbaccess.repository;org.terasoluna.batch.functionaltest.app.repository"
        template-ref="batchModeSqlSessionTemplate"/>

<!-- (4) -->
<bean id="writer" class="org.mybatis.spring.batch.MyBatisBatchItemWriter"
      p:statementId="org.terasoluna.batch.functionaltest.app.repository.plan.SalesPlanDetailRepository.create"
      p:sqlSessionTemplate-ref="batchModeSqlSessionTemplate"/>

    <batch:job id="updateMapperAndItemWriterBatchModeJob" job-repository="jobRepository">
        <batch:step id="updateMapperAndItemWriterBatchModeJob.step01">
            <batch:tasklet transaction-manager="jobTransactionManager">
            <batch:chunk reader="reader"
                         processor="updateCustomerItemProcessor"
                         writer="writer" commit-interval="10"/> <!-- (5) -->
        </batch:tasklet>
    </batch:step>
    <!-- omitted -->
</batch:job>

The contents for Mapper interface and Mapper XML are omitted as they are not different than the contents explained in MyBatisBatchItemWriter.

Description

Sr. No.

Description

(1)

Inject Mapper interface.

(2)

Generate DTO object, set update data and update database by returning DTO object.

(3)

Register Mapper XML.
By specifying batchModeSqlSessionTemplate set as BATCH in template-ref attribute, database access with ItemProcessor is BATCH. Here, when factory-ref="jobSqlSessionFactory" is set, it conflicts with the earlier mentioned restriction causing an exception at the time of executing MyBatisBatchItemWriter.

(4)

Define MyBatisBatchItemWriter.
Specify batchModeSqlSessionTemplate set as BATCH in sqlSessionTemplate-ref property.

(5)

Specify MyBatisBatchItemWriter defined in (4) in writer attribute.

How to use in tasklet model

Implementation example for updating (registering) database using Mapper interface in tasklet model is shown below.

Implementation example of Tasklet
@Component
public class OptimisticLockTasklet implements Tasklet {

    // (1)
    @Inject
    ExclusiveControlRepository repository;

    // omitted.

    @Override
    public RepeatStatus execute(StepContribution contribution,
            ChunkContext chunkContext) throws Exception {

        Branch branch = repository.branchFindOne(branchId);

        // (2)
        ExclusiveBranch exclusiveBranch = new ExclusiveBranch();
        exclusiveBranch.setBranchId(branch.getBranchId());
        exclusiveBranch.setBranchName(branch.getBranchName() + " - " + identifier);
        exclusiveBranch.setBranchAddress(branch.getBranchAddress() + " - " + identifier);
        exclusiveBranch.setBranchTel(branch.getBranchTel());
        exclusiveBranch.setCreateDate(branch.getUpdateDate());
        exclusiveBranch.setUpdateDate(new Timestamp(clock.millis()));
        exclusiveBranch.setOldBranchName(branch.getBranchName());

        // (3)
        int result = repository.branchExclusiveUpdate(exclusiveBranch);

        // omitted.

        return RepeatStatus.FINISHED;
    }
}
Bean definition
<!-- (4) -->
<mybatis:scan
        base-package="org.terasoluna.batch.functionaltest.ch05.exclusivecontrol.repository"
        factory-ref="jobSqlSessionFactory"/>

<batch:job id="taskletOptimisticLockCheckJob" job-repository="jobRepository">
    <batch:step id="taskletOptimisticLockCheckJob.step01">
        <batch:tasklet transaction-manager="jobTransactionManager"
                       ref="optimisticLockTasklet"> <!-- (5) -->
        </batch:tasklet>
    </batch:step>
</batch:job>

Mapper interface and Mapper XML are omitted.

Description
Sr. No. Description

(1)

Inject Mapper interface.

(2)

Generate DTO object and set update data.

(3)

Specify DTO object wherein update data is set, as an argument and execute update process in Mapper intrface.

(4)

Register Mapper XML.
Specify jobSqlSessionFactory set as REUSE in factory-ref attribute.

(5)

Inject Mapper interface and set Tasklet.

Database access with Listener

Database access with listener is often linked with other components. Depending on the listener to be used and the implementation method, it is necessary to prepare additional mechanism to hand over the fetched data in Mapper interface to other components.

There are following restrictions for implementing database access using Mapper interface in listener.

Restrictions in listener

There are same restictions as that in ItemProcessor even in Listener. In addition, for listeners, use cases requiring updates are difficult to think. Therefore, update processing is not recommended in the listener.

Replace update process assumed in listener
Job status management

It is performed by JobRepository of Spring Batch

Log output to database

It should be implemented in Appender of log. It should be managed separately from the transaction of job.

Here, an example of fetching the data before executing steps in StepExecutionListener and using the data fetched in ItemProcessor is shown.

Implementation example with Listener
public class CacheSetListener extends StepExecutionListenerSupport {

    // (1)
    @Inject
    CustomerRepository customerRepository;

    // (2)
    @Inject
    CustomerCache cache;

    @Override
    public void beforeStep(StepExecution stepExecution) {
        // (3)
        for(Customer customer : customerRepository.findAll()) {
            cache.addCustomer(customer.getCustomerId(), customer);
        }
    }
}
Application example with ItemProcessor
@Component
public class UpdateItemFromCacheProcessor implements
        ItemProcessor<SalesPerformanceDetail, SalesPlanDetail> {

    // (4)
    @Inject
    CustomerCache cache;

    @Override
    public SalesPlanDetail process(SalesPerformanceDetail readItem) throws Exception {
        Customer customer = cache.getCustomer(readItem.getCustomerId());  // (5)

        SalesPlanDetail writeItem = new SalesPlanDetail();

        // omitted.
        writerItem.setCustomerName(customer.getCustomerName); // (6)

        return writeItem;
    }
}
Cache class
// (7)
@Component
public class CustomerCache {

    Map<String, Customer> customerMap = new HashMap<>();

    public Customer getCustomer(String customerId) {
        return customerMap.get(customerId);
    }

    public void addCustomer(String id, Customer customer) {
        customerMap.put(id, customer);
    }
}
Bean definition
<!-- omitted -->

<!-- (8) -->
<mybatis:scan
        base-package="org.terasoluna.batch.functionaltest.app.repository"
        template-ref="batchModeSqlSessionTemplate"/>
<!-- (9) -->
<bean id="cacheSetListener"
      class="org.terasoluna.batch.functionaltest.ch05.dbaccess.CacheSetListener"/>

<!-- omitted -->

<batch:job id="DBAccessByItemListener" job-repository="jobRepository">
    <batch:step id="DBAccessByItemListener.step01">
        <batch:tasklet transaction-manager="jobTransactionManager">
            <batch:chunk reader="reader"
                         processor="updateItemFromCacheProcessor"
                         writer="writer" commit-interval="10"/> <!-- (10) -->
            <!-- (11) -->
            <batch:listeners>
                <batch:listener ref="cacheSetListener"/>
            </batch:listeners>
        </batch:tasklet>
    </batch:step>
</batch:job>
Description

Sr. No.

Description

(1)

Inject Mapper interface.

(2)

Inject a bean for caching data acquired from the Mapper interface.

(3)

Get data from the Mapper interface and cache it at the listener.
In this case, I/O is reduced and processing efficiency is improved by creating a cache before step execution with StepExecutionListener#beforeStep and referring to the cache in the subsequent processing.

(4)

Inject the same bean as the cache set in (2).

(5)

Get corresponding data from the cache.

(6)

Reflect the data from the cache in the update data.

(7)

Implement the cache class as a component.
The Bean scope is singleton in here. Please set according to job.

(8)

Register Mapper XML.
Specify batchModeSqlSessionTemplate set as BATCH in template-ref attribute.

(9)

Define the listener that uses the Mapper interface.

(10)

Specify ItemProcessor that uses cache.

(11)

Register the listener defined in (9).

Using SqlSessionFactory with the Listener

In the above example, batchModeSqlSessionTemplate is set, but jobSqlSessionFactory also can be set.

For listeners that run outside the scope of chunks, since it is processed outside the transaction, setting jobSqlSessionFactory does not matter.

How To Extend

Updating multiple tables in CompositeItemWriter

In a chunk model, when multiple tables are to be updated for 1 input data, it can be achieved by using CompositeItemWriter provided by Spring Batch and linking MyBatisBatchItemWriter corresponding to each table.

An implementation example wherein two tables of sales plan and actual sales are updated, is shown here.

Implementation example of ItemProcessor
@Component
public class SalesItemProcessor implements ItemProcessor<SalesPlanDetail, SalesDTO> {
    @Override
    public SalesDTO process(SalesPlanDetail item) throws Exception { // (1)

        SalesDTO salesDTO = new SalesDTO();

        // (2)
        SalesPerformanceDetail spd = new SalesPerformanceDetail();
        spd.setBranchId(item.getBranchId());
        spd.setYear(item.getYear());
        spd.setMonth(item.getMonth());
        spd.setCustomerId(item.getCustomerId());
        spd.setAmount(new BigDecimal(0L));
        salesDTO.setSalesPerformanceDetail(spd);

        // (3)
        item.setAmount(item.getAmount().add(new BigDecimal(1L)));
        salesDTO.setSalesPlanDetail(item);

        return salesDTO;
    }
}
Implementation example of DTO
public class SalesDTO implements Serializable {

    // (4)
    private SalesPlanDetail salesPlanDetail;

    // (5)
    private SalesPerformanceDetail salesPerformanceDetail;

    // omitted
}
Implementation example of MapperXML
<mapper namespace="org.terasoluna.batch.functionaltest.ch05.dbaccess.repository.SalesRepository">

    <select id="findAll" resultType="org.terasoluna.batch.functionaltest.app.model.plan.SalesPlanDetail">
        <![CDATA[
        SELECT
            branch_id AS branchId, year, month, customer_id AS customerId, amount
        FROM
            sales_plan_detail
        ORDER BY
            branch_id ASC, year ASC, month ASC, customer_id ASC
        ]]>
    </select>

    <!-- (6) -->
    <update id="update" parameterType="org.terasoluna.batch.functionaltest.ch05.dbaccess.SalesDTO">
        <![CDATA[
        UPDATE
            sales_plan_detail
        SET
            amount = #{salesPlanDetail.amount}
        WHERE
            branch_id = #{salesPlanDetail.branchId}
        AND
            year = #{salesPlanDetail.year}
        AND
            month = #{salesPlanDetail.month}
        AND
            customer_id = #{salesPlanDetail.customerId}
        ]]>
    </update>

    <!-- (7) -->
    <insert id="create" parameterType="org.terasoluna.batch.functionaltest.ch05.dbaccess.SalesDTO">
        <![CDATA[
        INSERT INTO
            sales_performance_detail(
                branch_id,
                year,
                month,
                customer_id,
                amount
            )
        VALUES (
            #{salesPerformanceDetail.branchId},
            #{salesPerformanceDetail.year},
            #{salesPerformanceDetail.month},
            #{salesPerformanceDetail.customerId},
            #{salesPerformanceDetail.amount}
        )
        ]]>
    </insert>

</mapper>
Application example of CompositeItemWriter
<!-- reader using MyBatisCursorItemReader -->
<bean id="reader" class="org.mybatis.spring.batch.MyBatisCursorItemReader"
      p:queryId="org.terasoluna.batch.functionaltest.ch05.dbaccess.repository.SalesRepository.findAll"
      p:sqlSessionFactory-ref="jobSqlSessionFactory"/>

<!-- writer MyBatisBatchItemWriter -->
<!-- (8) -->
<bean id="planWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter"
      p:statementId="org.terasoluna.batch.functionaltest.ch05.dbaccess.repository.SalesRepository.update"
      p:sqlSessionTemplate-ref="batchModeSqlSessionTemplate"/>

<!-- (9) -->
<bean id="performanceWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter"
      p:statementId="org.terasoluna.batch.functionaltest.ch05.dbaccess.repository.SalesRepository.create"
      p:sqlSessionTemplate-ref="batchModeSqlSessionTemplate"/>

<!-- (10) -->
<bean id="writer" class="org.springframework.batch.item.support.CompositeItemWriter">
    <property name="delegates">
      <!-- (11)-->
        <list>
            <ref bean="performanceWriter"/>
            <ref bean="planWriter"/>
        </list>
    </property>
</bean>

<!-- (12) -->
<batch:job id="useCompositeItemWriter" job-repository="jobRepository">
    <batch:step id="useCompositeItemWriter.step01">
        <batch:tasklet transaction-manager="jobTransactionManager">
            <batch:chunk reader="reader"
                         processor="salesItemProcessor"
                         writer="writer" commit-interval="3"/>
        </batch:tasklet>
    </batch:step>
</batch:job>
Description

Sr. No.

Description

(1)

Implement ItemProcessor with DTO as output which retains each entity for updating both the tables for input data.
Since different objects cannot be passed in ItemWriter for updating 2 tables, a DTO which consolidates objects necessary for update is defined.

(2)

Create an entity for creating a new actual sales record (SalesPerformanceDetail) and store in DTO.

(3)

Update input data for updating sales plan which is also input data (SalesPlanDetail) and store it in DTO.

(4)

Define in DTO so as to retain a sales plan (SalesPlanDetail).

(5)

Define in DTO so as to retain actual sales record (SalesPerformanceDetail).

(6)

Define a SQL to update sales plan table (sales_plan_detail) in sales plan (SalesPlanDetail) fetched from DTO.

(7)

Define a SQL to create a new actual sales table (sales_performance_detail) in actual sales (SalesPlanDetail) fetched from DTO.

(8)

Define MyBatisBatchItemWriter which updates sales plan table (sales_plan_detail).

(9)

Define MyBatisBatchItemWriter which creates a new actual sales table (sales_performance_detail).

(10)

Define CompositeItemWriter in order to execute (8) and (9) sequentially.

(11)

Set (8) and (9) in <list> tag. ItemWriter is executed in the specified order.

(12)

Specify the Bean defined in (10), in writer attribute of chunk. Specify ItemProcessor of (1) in processor attribute.

It can also be updated for multiple data sources by using it together with org.springframework.data.transaction.ChainedTransactionManager which is explained in Output to multiple data sources (1 step).

Further, since CompositeItemWriter can be linked in case of ItemWriter implementation, it can be done along with database output and file output by setting MyBatisBatchItemWriter and FlatFileItemWriter.

How to specify search condition

When you want to search by specifying search condition while accessing database, search conditions can be specified by fetching values from job parameters in Map format in Bean definition and setting key. An example of job start command that specifies job parameters and an implementation example are shown below.

Job start command when job parameters are specified.
java -cp ${CLASSPATH} org.springframework.batch.core.launch.support.CommandLineJobRunner
 /META-INF/job/job001 job001 year=2017 month=12
Implementation example of MapperXML
<!-- (1) -->
<select id="findByYearAndMonth"
    resultType="org.terasoluna.batch.functionaltest.app.model.performance.SalesPerformanceSummary">
    <![CDATA[
    SELECT
        branch_id AS branchId, year, month, amount
    FROM
        sales_performance_summary
    WHERE
        year = #{year} AND month = #{month}
    ORDER BY
        branch_id ASC
    ]]>
</select>

<!-- omitted -->
Bean definition
<!-- omitted -->

<!-- (2) -->
<bean id="reader"
      class="org.mybatis.spring.batch.MyBatisCursorItemReader" scope="step"
      p:queryId="org.terasoluna.batch.functionaltest.ch08.parallelandmultiple.repository.SalesSummaryRepository.findByYearAndMonth"
      p:sqlSessionFactory-ref="jobSqlSessionFactory">
    <property name="parameterValues"> <!-- (3) -->
        <map>
            <!-- (4) -->
            <entry key="year" value="#{jobParameters['year']}" value-type="java.lang.Integer"/>
            <entry key="month" value="#{jobParameters['month']}" value-type="java.lang.Integer"/>

            <!-- omitted -->
        </map>
    </property>
</bean>

<!-- omitted -->
Description
Sr. No. Description

(1)

Specify search condition and define the SQL to be fetched.

(2)

Define ItemReader to fetch data from database.

(3)

Set parameterValues in property name.

(4)

Specify search conditions by fetching values to be set in search condition from job parameters and by setting as key. Since SQL arguments are defined in numerical value, they are passed by converting to Integer by value-type.

How to specify search by StepExectionContext

When search condition is to be specified in pre-process of job such as @beforeStep, the values can be fetched same as JobParameters by setting to StepExecutionContext.

TERASOLUNA Batch Framework for Java (5.x) Development Guideline - version 5.1.1.RELEASE, 2018-3-16