web-dev-qa-db-ja.com

複数のソースから読み取られたSpringBatchジョブ

複数のデータベースからアイテムを読み取るにはどうすればよいですか?私はそれがファイルから可能であることをすでに知っています。
次の例は、複数のファイルからの読み取りで機能します

...
<job id="readMultiFileJob" xmlns="http://www.springframework.org/schema/batch">
    <step id="step1">
    <tasklet>
        <chunk reader="multiResourceReader" writer="flatFileItemWriter"
            commit-interval="1" />
    </tasklet>
    </step>
</job>
...
<bean id="multiResourceReader"
    class=" org.springframework.batch.item.file.MultiResourceItemReader">
    <property name="resources" value="file:csv/inputs/domain-*.csv" />
    <property name="delegate" ref="flatFileItemReader" />
</bean>
...

このような3つの豆。

<bean id="database2" class="org.springframework.batch.item.database.JdbcCursorItemReader">
    <property name="name" value="database2Reader" />
    <property name="dataSource" ref="dataSource2" />
    <property name="sql" value="select image from object where image like '%/images/%'" />
    <property name="rowMapper">
        <bean class="sym.batch.ImagesRowMapper2" />
    </property>
</bean>
15
xedo

要求したことを実行するすぐに使用できるコンポーネントはありません。唯一の解決策は、JdbcCursorItemReader(またはHibernateCursorItemReaderまたは任意の汎用ItemReader実装)に委任するカスタム_ItemReader<>_を作成することです。
必要なもの(データソース、セッション、実際のデータベースリーダー)をすべて準備し、委任されたすべてのリーダーをカスタムリーダーにバインドする必要があります。

編集:ItemReader.read()の再帰を使用してループをシミュレートし、リーダーを維持し、ジョブの再起動全体で状態を委任する必要があります。

_class MyItemReader<T> implements ItemReader<T>, ItemStream {
  private ItemReader[] delegates;
  private int delegateIndex;
  private ItemReader<T> currentDelegate;
  private ExecutionContext stepExecutionContext;

  public void setDelegates(ItemReader[] delegates) {
    this.delegates = delegates;
  }

  @BeforeStep
  private void beforeStep(StepExecution stepExecution) {
    this.stepExecutionContext = stepExecution.getExecutionContext();
  }

  public T read() {
    T item = null;
    if(null != currentDelegate) {
      item = currentDelegate.read();
      if(null == item) {
        ((ItemStream)this.currentDelegate).close();
        this.currentDelegate = null;
      }
    }
    // Move to next delegate if previous was exhausted!
    if(null == item && this.delegateIndex< this.delegates.length) {
      this.currentDelegate = this.delegates[this.currentIndex++];
      ((ItemStream)this.currentDelegate).open(this.stepExecutionContext);
      update(this.stepExecutionContext);
      // Recurse to read() to simulate loop through delegates
      item = read();
    }
    return item;
  }

  public void open(ExecutionContext ctx) {
    // During open restore last active reader and restore its state
    if(ctx.containsKey("index")) {
      this.delegateIndex = ctx.getInt("index");
      this.currentDelegate = this.delegates[this.delegateIndex];
      ((ItemStream)this.currentDelegate ).open(ctx);
    }
  }

  public void update(ExecutionContext ctx) {
    // Update current delegate index and state
    ctx.putInt("index", this.delegateIndex);
    if(null != this.currentDelegate) {
      ((ItemStream)this.currentDelegate).update(ctx);
    }
  }

  public void close(ExecutionContext ctx) {
    if(null != this.currentDelegate) {
      ((ItemStream)this.currentDelegate).close();
  }
}
_

_<bean id="myItemReader" class=path.to.MyItemReader>
  <property name="delegates">
    <array>
      <ref bean="itemReader1"/>
      <ref bean="itemReader2"/>
      <ref bean="itemReader3"/>
    </array>
  </property>
</bean>
_

EDIT2:プロパティを設定することを忘れないでくださいname;これは、MyItemReader.read()が正しく機能するために必要です。

_<bean id="itemReader1" class="JdbcCursorItemReader">
  <property name="name" value="itemReader1" />
  <!-- Set other properties -->
</bean>
_
14

すべての場合に適しているとは限らないが、多くの場合に役立つ簡単な回避策を提案します。

簡単に定義します:

  • データベースごとに1つずつ、2つのリーダー
  • 2ステップ
  • 2つのステップの両方を含む1つのジョブ

2つのステップはほぼ同じで、同じプロセッサとライターを参照しますが、リーダーが異なります。それらは連続して呼び出されます。

このセットアップが機能するかどうかは、プロセッサとライターによって異なります(異なる手順で呼び出されたときに正しく機能するかどうか)。私の場合、両方のステップで同じファイルに書き込めるように、ライターにappendAllowed=trueを設定するだけで十分でした。

1
stefan.m

トリッキーな方法を提案します。 1つがmysqlデータソースのテーブルがベースであり、そのテーブルのすべての行が他のmysqlデータソーステーブルの行に対応していると仮定した場合(異なるデータソースにある結合テーブルなど)、バッチジョブアイテムリーダーでそれを行うことができます。この方法の例;

SpringDataSource構成;

<bean id="mySqlDataSource1" class="org.Apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${database1.driverClassName}"/>
    <property name="url" value="${database1.url}"/>
    <property name="username" value="${database1.username}"/>
    <property name="password" value="${database1.password}"/>
    <property name="validationQuery" value="${database1.validationQuery}"/>
</bean>

<bean id="mySqlDataSource2" class="org.Apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${database2.driverClassName}"/>
    <property name="url" value="${database2.url}"/>
    <property name="username" value="${database2.username}"/>
    <property name="password" value="${database2.password}"/>
    <property name="validationQuery" value="${database2.validationQuery}"/>
</bean>

あなたのbatch-job.xml

<bean id="multiDatasorceReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
    <property name="dataSource" ref="mySqlDataSource1" />
    <property name="rowMapper" ref="multiDatasourceRowMapper" />
    <property name="sql">
        <value>
            SELECT * FROM xyz
        </value>
    </property>
</bean>

<bean id="multiDatasourceRowMapper" class="yourpackage.MultiDatasourceRowMapper" scope="step">
    <property name="secondDataSource" ref="mySqlDataSource2" />
    <property name="secondSql">
        <value>
            SELECT * FROM abc
        </value>
    </property>
</bean>

RowMapperは次のようになります。

public class MultiDatasourceRowMapper implements RowMapper<String> {

    private DataSource secondDataSource;

    private String secondSql;

    public String mapRow(ResultSet rs, int arg1) throws SQLException {
        Connection conn = secondDataSource.getConnection();
        PreparedStatement prep = conn.prepareStatement(secondSql); 

        // Do Something

        return "";
    }

    public void setSecondDataSource(DataSource secondDataSource) {
        this.secondDataSource = secondDataSource;
    }

    public void setSecondSql(String secondSql) {
        this.secondSql = secondSql;
    }

}
0
nsylmz