QA@IT
«質問へ戻る

質問を投稿

Spring-Batchの同一Step内のReader/Writerで異なるデータベースは参照できますか?

Spring-Batch3.0とMyBatis3.3を使用して、データを同一Step内のReaderで読み込み、Processorで加工して、WriterでDB出力する処理を行っています。
DBはMySQLで、Master/Slaveの二重構成になっており、Masterは更新用、Slaveは検索用として使用しています。
Spring-Batchの同一Step内で
ReaderではSlave用のDBを検索させ、
WriterではMaster用のDBを更新させたいのですが、以下(1)(2)の方法ではDBを切り替える事ができませんでした。

そもそも、Spring-Batchでは、ReaderとWriterで別のDBを参照することができないのでしょうか?
できないとしたらどんな理由が考えられるのでしょうか?
何かご存じの方がいらっしゃれば教えていただきたいと思います。

(1)DBReplicationと@Transactionalアノテーションで制御
以下の設定をしても、常にmaster側のDBを参照している。

JDBCプロパティに以下の定義をします。
batch.mysql.jdbc.driver=com.mysql.jdbc.ReplicationDriver
batch.mysql.jdbc.url=jdbc:mysql:replication://masterhost:3306,slavehost:3306/db名

バッチの制御用XMLには、以下のトランザクション定義をして、バッチからも使えるようにします。アノテーションも使えるようにします。

<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <beans:property name="dataSource" ref="上記プロパティのデータソース" />
</beans:bean>

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>

バッチからは、上記のtransactionManagerを使うようにします。

<job id="DeliveryLogUpdate" xmlns="http://www.springframework.org/schema/batch" restartable="false">
    <step id="DeliveryLogUpdateStep1" parent="faultTolerantStep"> 
        <tasklet transaction-manager="transactionManager">

バッチのJavaコードで、DB検索だけを行うメソッドには
@Transactional(readOnly = true)
を付け、
DB更新を行うメソッドには
@Transactional(readOnly = false,isolation=Isolation.REPEATABLE_READ, rollbackFor = Exception.class)
を付ける。

(2)DBレプリケーションを自作する
http://kwonnam.pe.kr/wiki/java/jdbc/replication
の方式。
下記コードを書いて、transactionManagerのデータソースとして登録。
ログを吐かせた所、Spring-batchでは以下の箇所を呼ぶのは、
Step実行前の1回だけで、Reader/Writerの実行前に切り替える事はできない。

public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "slave" : "master";
}
}

Spring-Batch3.0とMyBatis3.3を使用して、データを同一Step内のReaderで読み込み、Processorで加工して、WriterでDB出力する処理を行っています。
DBはMySQLで、Master/Slaveの二重構成になっており、Masterは更新用、Slaveは検索用として使用しています。
Spring-Batchの同一Step内で
ReaderではSlave用のDBを検索させ、
WriterではMaster用のDBを更新させたいのですが、以下(1)(2)の方法ではDBを切り替える事ができませんでした。

そもそも、Spring-Batchでは、ReaderとWriterで別のDBを参照することができないのでしょうか?
できないとしたらどんな理由が考えられるのでしょうか?
何かご存じの方がいらっしゃれば教えていただきたいと思います。

(1)DBReplicationと@Transactionalアノテーションで制御
以下の設定をしても、常にmaster側のDBを参照している。

JDBCプロパティに以下の定義をします。
batch.mysql.jdbc.driver=com.mysql.jdbc.ReplicationDriver
batch.mysql.jdbc.url=jdbc:mysql:replication://masterhost:3306,slavehost:3306/db名


バッチの制御用XMLには、以下のトランザクション定義をして、バッチからも使えるようにします。アノテーションも使えるようにします。

    <beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <beans:property name="dataSource" ref="上記プロパティのデータソース" />
    </beans:bean>

    <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>

バッチからは、上記のtransactionManagerを使うようにします。

	<job id="DeliveryLogUpdate" xmlns="http://www.springframework.org/schema/batch" restartable="false">
		<step id="DeliveryLogUpdateStep1" parent="faultTolerantStep"> 
			<tasklet transaction-manager="transactionManager">

バッチのJavaコードで、DB検索だけを行うメソッドには
@Transactional(readOnly = true)
を付け、
DB更新を行うメソッドには
@Transactional(readOnly = false,isolation=Isolation.REPEATABLE_READ, rollbackFor = Exception.class)
を付ける。


(2)DBレプリケーションを自作する
http://kwonnam.pe.kr/wiki/java/jdbc/replication
の方式。
下記コードを書いて、transactionManagerのデータソースとして登録。
ログを吐かせた所、Spring-batchでは以下の箇所を呼ぶのは、
Step実行前の1回だけで、Reader/Writerの実行前に切り替える事はできない。

public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "slave" : "master";
    }
}