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 のみが返されます。