MyBatis-Spring 1.1.0 以降では、 Spring Batch を構築するための Bean として MyBatisPagingItemReader と MyBatisBatchItemWriter が用意されています。 これらの Bean 及びこのドキュメントは、いずれも Spring Batch の一部として提供されていた iBATIS 2.x 用のものを移植したものです。
NOTE ここで扱うのは Spring Batch を使ったバッチ処理で、MyBatis の SqlSession を利用したバッチ処理ではありません。
この Bean は、MyBatis を利用してデータベースからページ単位でレコードを読み出す ItemReader です。
結果を取得する際は setQueryId プロパティで指定したクエリが実行されます。 1ページあたりの件数は setPageSize プロパティで指定することができます。 read() メソッドが呼び出されると、必要に応じて追加のページを取得するクエリが実行されます。 実行されるクエリでは、Reader によって提供されるページング処理を行う際に必要となるパラメーターを使って期待される結果を返す SQL 文を記述することになります(実際の SQL 文は方言依存です)。 提供されるパラメーターは次の通りです。
これらのパラメーターは、例えば次のように SELECT 文中で指定することができます。
<select id="getEmployee" resultMap="employeeBatchResult">
SELECT id, name, job FROM employees ORDER BY id ASC LIMIT #{_skiprows}, #{_pagesize}
</select>設定例:
<bean id="reader" class="org.mybatis.spring.batch.MyBatisPagingItemReader"> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> <property name="queryId" value="getEmployee" /> </bean>
さらに複雑な例:
<bean id="dateBasedCriteriaReader"
class="org.mybatis.spring.batch.MyBatisPagingItemReader"
p:sqlSessionFactory-ref="batchReadingSessionFactory"
p:parameterValues-ref="datesParameters"
p:queryId="com.my.name.space.batch.ExampleMapper.queryUserInteractionsOnSpecificTimeSlot"
p:pageSize="${200}"
scope="step"/>
<util:map id="datesParameters" key-type="or.joda.time.DateTime" scope="step">
<entry key="yesterday"
value="#{jobExecutionContext['EXTRACTION_START_DATE']}"/>
<entry key="today"
value="#{jobExecutionContext['TODAY_DATE']}"/>
<entry key="first_day_of_the_month"
value="#{jobExecutionContext['FIRST_DAY_OF_THE_MONTH_DATE']}"/>
<entry key="first_day_of_the_previous_month"
value="#{jobExecutionContext['FIRST_DAY_OF_THE_PREVIOUS_MONTH_DATE']}"/>
</util:map>The previous example makes use of a few different things:
SqlSessionTemplate のバッチ機能を使って渡された一連のアイテムを処理する ItemWriter です。 SqlSessionFactory は ExecutorType.BATCH を使って設定する必要があります。
write() の呼び出し時に実行するステートメントの ID を指定しておく必要があります。 write() はトランザクション内で呼び出されることを前提としています。
設定例:
<bean id="writer" class="org.mybatis.spring.batch.MyBatisBatchItemWriter"> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> <property name="statementId" value="updateEmployee" /> </bean>
Composite Writer を使って複数のテーブルに書き込む(注意事項あり)
このテクニックを使うには MyBatis 3.2 以降が必要です。それ以前のバージョンには 問題 があるため、Writer が期待通りに動作しません。
まず、Interaction と1対1の関係にある InteractionMetadata と、これらとは独立した VisitorInteraction および CustomerInteraction を保持する Item クラスを用意します。
public class InteractionRecordToWriteInMultipleTables {
private final VisitorInteraction visitorInteraction;
private final CustomerInteraction customerInteraction;
private final Interaction interaction;
// ...
}
public class Interaction {
private final InteractionMetadata interactionMetadata;
}CompositeItemWriter の設定では、それぞれのオブジェクトの writer を順番に呼び出すように設定します。 この例では Interaction をアップデートするためのキーを取得するため、InteractionMetadata を先に書き込む必要があります。
<bean id="interactionsItemWriter" class="org.springframework.batch.item.support.CompositeItemWriter">
<property name="delegates">
<list>
<ref bean="visitorInteractionsWriter"/>
<ref bean="customerInteractionsWriter"/>
<!-- Order is important -->
<ref bean="interactionMetadataWriter"/>
<ref bean="interactionWriter"/>
</list>
</property>
</bean>それぞれの writer を必要に応じて設定します。例えば Interaction と InteractionMetadata の設定は次のようになります。
<bean id="interactionMetadataWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter" p:sqlSessionTemplate-ref="batchSessionTemplate" p:statementId="com.my.name.space.batch.InteractionRecordToWriteInMultipleTablesMapper.insertInteractionMetadata"/> <bean id="interactionWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter" p:sqlSessionTemplate-ref="batchSessionTemplate" p:statementId="com.my.name.space.batch.InteractionRecordToWriteInMultipleTablesMapper.insertInteraction"/>
reader の場合と同様、 statementId はネームスペースを含むステートメント ID です。
Mapper ファイルにステートメントを定義します。
<insert id="insertInteractionMetadata"
parameterType="com.my.batch.interactions.item.InteractionRecordToWriteInMultipleTables"
useGeneratedKeys="true"
keyProperty="interaction.interactionMetadata.id"
keyColumn="id">
<!-- the insert statement using #{interaction.interactionMetadata.property,jdbcType=...} -->
</insert>
<insert id="insertInteraction"
parameterType="com.my.batch.interactions.item.InteractionRecordToWriteInMultipleTables"
useGeneratedKeys="true"
keyProperty="interaction.id"
keyColumn="id">
<!--
the insert statement using #{interaction.property,jdbcType=...} for regular properties
and #{interaction.interactionMetadata.property,jdbcType=...} for the InteractionMetadata property
-->
</insert>はじめに insertInteractionMetadata が呼ばれ、その際に取得した主キーを使って親となる Interaction を insertInteraction を使って書き込むことができます。
JDBC ドライバによって動作が異なるので注意が必要です。例えば MySQL の JDBC ドライバは作成された全ての行の ID を返しますが、H2 バージョン 1.3.168 ではバッチモードでも最後に作成された行の ID のみが返されます。