복붙노트

[SPRING] 다중 소스에서 읽은 일괄 작업 일괄 처리

SPRING

다중 소스에서 읽은 일괄 작업 일괄 처리

여러 데이터베이스에서 항목을 읽으려면 어떻게해야합니까? 파일에서 가능하다는 것을 이미 알고 있습니다. 다음 예제는 여러 파일에서 읽기에 사용됩니다.

...
<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>

해결법

  1. ==============================

    1.요청한 것을 수행하는 즉시 사용할 수있는 구성 요소가 없습니다. 유일한 해결책은 JdbcCursorItemReader (또는 HibernateCursorItemReader 또는 임의의 일반적인 ItemReader 구현)에 위임하는 사용자 정의 ItemReader <>를 작성하는 것입니다. 필요한 모든 데이터 (데이터 소스, 세션, 실제 데이터베이스 리더)를 준비하고 모든 위임 된 독자를 맞춤형 리더에 바인딩해야합니다.

    요청한 것을 수행하는 즉시 사용할 수있는 구성 요소가 없습니다. 유일한 해결책은 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 : 속성 이름을 설정하는 것을 잊지 마십시오; MyItemReader.read ()가 올바르게 작동하도록하려면 이것이 필요합니다.

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

    2.나는 모든 경우에 적합하지 않을 수도있는 간단한 해결책을 제안하지만, 많은 경우에 유용 할 것이다.

    나는 모든 경우에 적합하지 않을 수도있는 간단한 해결책을 제안하지만, 많은 경우에 유용 할 것이다.

    간단히 정의하십시오 :

    2 단계는 거의 동일하며 동일한 프로세서와 작성자를 참조하지만 서로 다른 독자가 있습니다. 그들은 연속적으로 부름을받을 것입니다.

    이 설정이 작동하는지 여부는 프로세서와 작성기 (다른 단계에서 호출 할 때 올바르게 작동하는지 여부)에 따라 다릅니다. 필자의 경우 appendAllowed = true를 작성자에게 설정하면 충분하므로 두 단계 모두 동일한 파일에 쓸 수 있습니다.

  3. ==============================

    3.나는 까다로운 방법을 제안한다. 우리가 하나의 mysql 데이터 소스의 테이블이 기본이고 그 테이블의 모든 행이 (다른 데이터 소스에있는 조인 테이블처럼) 다른 mysql 데이터 소스 테이블의 행과 일치한다고 가정하면 배치 작업 itemreader에서 할 수있다. 이런 식의 예.

    나는 까다로운 방법을 제안한다. 우리가 하나의 mysql 데이터 소스의 테이블이 기본이고 그 테이블의 모든 행이 (다른 데이터 소스에있는 조인 테이블처럼) 다른 mysql 데이터 소스 테이블의 행과 일치한다고 가정하면 배치 작업 itemreader에서 할 수있다. 이런 식의 예.

    스프링 데이터 소스 설정;

    <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;
        }
    
    }
    
  4. from https://stackoverflow.com/questions/21304364/spring-batch-job-read-from-multiple-sources by cc-by-sa and MIT license