06. spring batch - 스프링 배치 처리 샘플 (chunk 처리 : 파일 읽기)

2021. 4. 13. 17:19spring

728x90
반응형

가. 목표

SpringBatch에서 제공하는 소스를 가능한 이용해서 기능을 구현했다. ( 2012.03.17 )

  Java 1.6이상

  Spring 4.2.4

  SpringSecurity 3.1

  SpringBatch 3.0.7

  SpringModules 0.9 ⇒ deprecated

  Struts 1.3.10

  Easy Mock 3.0

  JUnit 4.4

  • csv 파일을 읽어서 다른 파일에 쓰기 샘플. 이때 열을 바꿔서 출력.

- SpringBatch로 읽어들인 파일 데이터를 FieldSet이라는 클래스에 보존한다.

- FieldSet 에서 POJO 클래스로 값을 옮겨 적는 것을 매핑한다라고 한다.

나. 스프링배치의 기반에 관한 설정 /back-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/batch
  http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

  <bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator"
   p:jobLauncher-ref="jobLauncher" p:jobExplorer-ref="jobExplorer"
   p:jobRepository-ref="jobRepository" p:jobRegistry-ref="jobRegistry" />

  <bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
   p:dataSource-ref="dataSource" />

  <bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />

  <bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
     <property name="jobRegistry" ref="jobRegistry"/>
  </bean>

  <bean id="jobLauncher"
    class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
     <property name="jobRepository" ref="jobRepository" />
  </bean>

  <bean id="jobRepository"
    class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
    p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager"
    p:maxVarCharLength="2000"
    p:isolationLevelForCreate="ISOLATION_SERIALIZABLE" />

  <!-- DB設定 -->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="XXX.driver" />
    <property name="url" value="jdbc.XXX.url" />
    <property name="username" value="XXX" />
    <property name="password" value="XXX" />
  </bean>

  <bean id="transactionManager"
   class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
    <property name="dataSource" ref="dataSource" />
  </bean>

</beans>

다. 설정파일 샘플 /job-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/batch
    http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
">

<!-- SpringBatch의 기반부분에 관한 설정의 임포트 -->
<import resource="classpath:/back-context.xml" />

<!-- ジョブの設定 -->
<job id="job1" xmlns="http://www.springframework.org/schema/batch"
 incrementer="jobParametersIncrementer">
  <step id="step1" parent="simpleStep">
    <tasklet>
      <chunk reader="fileItemReader" writer="fileItemWriter" />
    </tasklet>
  </step>
</job>

<!-- enables the functionality of JobOperator.startNextInstance(jobName) -->
<bean id="jobParametersIncrementer"
  class="org.springframework.batch.core.launch.support.RunIdIncrementer" />
<bean id="simpleStep"
  class="org.springframework.batch.core.step.item.SimpleStepFactoryBean"
  abstract="true">
    <property name="jobRepository" ref="jobRepository" />
    <property name="commitInterval" value="1" />
</bean>

<bean id="fileItemReader"
 class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
    <property name="resource" value="file:c:/test/data.csv" />
    <property name="lineMapper">
      <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
        <property name="lineTokenizer">
          <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
            <property name="names" value="num,name" />
          </bean>
        </property>
        <property name="fieldSetMapper">
          <bean class="test.batch.item.MemberFieldSetMapper" />
        </property>
      </bean>
    </property>
</bean>

<!-- 열을 변경해서 다른 파일에 출력하는 Writer -->
<bean id="fileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
  <property name="resource" value="file:c:/test/test.txt" />
  <property name="lineAggregator">
    <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
       <property name="fieldExtractor">
          <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor" >
             <property name="names" value="name,num" />
          </bean>
       </property>
    </bean> 
  </property>
</bean>

</beans>
  1. 설정파일 설명
    가. fileItemReader (FlatFileItemReader 클래스)

<FlatFileItemReader의 프로퍼티>

프로퍼티 명 형 (타입) 내용  
comments String[] 설정한 값이 행의 맨앞에 있으면 코맨트행으로 간주
encoding String 문자인코딩, 초기값 = "ISO-8859-1"
lineMapper LineMapper 오브젝트에 매핑하는 방법 지정
linesToSkip int 무시 (스킵)하는 행수 지정
recordSeparatorPolicy RecordSeparatorPolicy ""으로 묶인 문자열 중에 개행문자가 있어도 하나의 레코드로 간주하여 읽어들일지를 결정.
resource Resource 읽어들일 파일 패스 지정.
skippedLinesCallback LineCallbackHandler 스킵한 행을 여기에 지정한 콜백에 전달. 만약, linesToSkip에 2를 설정한 경우, 콜백을 2회 호출.
strict boolean true로 설정하면 stict mode로 변환.
resource에 지정한 리소스가 발견되지 않은 경우, exception 발생.

lineMapper 프로퍼티를 변경하는 것으로 구분자 문자 변경하수도, 고정문자열에도 대응 가능하다.

여기서 사용되는 DelimitedLineTokenizer, FixedLengthTokenizer, PatternMatchingCompositeLineTokenizer 등의 부품에 대해서는 javadoc를 참조하시오.

또한 복수행으로 하나의 데이터로 된 경우에도 대응 가능하다.

고정문자열을 읽어 들이느 tokenizer의 사례

<bean id="fixedLengthLineTokenizer"
      class="org.springframework.batch.io.file.transform.FixedLengthTokenizer">
    <property name="names" value="num,name,age" />
    <property name="columns" value="1-7, 7-18, 19-21" />
</bean>

 

나. fileItemWriter (FlatFileItemWriter클래스)

이 클래스는 파일에 데이터를 쓰는 데 이용된다.

lineAggregator 프로퍼티에 설정되어 있는 것은 구분자문자에 의해 데이터를 쓰는 클래스이다.

물론 구분자 문자도 자유롭게 변경 가능하다.

또한 FormatterLineAggregator를 사용하면 위치를 자유롭게 변경하는 것도, 서식변환하는 것도 가능하다.

<FlatFileItemWriter의 프로퍼티>

프로퍼티 명 형 (타입) 내용  
appendAllowed boolean true일 경우, append mode로 처리한다.
이 프로퍼티가 설정되면 shouldDeleteIfExists 프로퍼티는 자동적으로 false로 변경된다. 그러므로 이경우에는 shouldDeleteIfExists프로퍼티를 설정하면안된다. 초기값은 false
shouldDeleteIfEmpty boolean 처리가 종료 될 때 파일에 헤더, 풋터 이외에 1행도 write안된 경우 파일을 삭제할지 여부를 지정한다.
true의 경우, 파일 삭제, 초기값은 false
saveState boolean ItemStream이 update 메소드를 호출하는 동안 ExecutionContext에 상태를 보존할지 말지를 지정한다.
만약 false를 설정한 경우, restart 했을 때 언제나 가장 최초에서 시작하는 것으로 동작한다. 결국 에러가 난 위치부터 시작하지 않는다.
headerCallback FlatFileHeaderCallback 첫번째 item을 처리하기 전에 이 callback이 호출된다.
footerCallback FlatFileFooterCallback 마지막 item을 처리된 이후에 이 callback이 호출된다.
transactional boolean 만약 트랜잭션이 active인 경우, 버퍼에 쓰기를 지연하는 것을 지시한다. 초기값은 true. 
lineSeparator String 개행문자를 설정한다. 초기값은 java 시스템 프로퍼티에 설정한 개행문자이다. 시스템프로퍼티에 통상 OS의 초기 개행문자가 설정되어 있을 것.
lineAggregator LineAggregator 아이템을 파일의 1행에 전개한는 클래스를 지정
Resource Resource 출력파일을 지정
encoding
String 출력파일의 인코딩을 지정

다. POJO 클래스, test.batch.item.Member.java

public class Member {
  private int num;
  private String name;

  public int getNum() {
     return num;
  }

  public void setNum(int num) {
     this.num = num;
  }

  public String getName() {
     return name;
  }

  public void setName(String name) {
     this.name = name;
  }
}

라. POJO 에 값설정 매퍼 클래스 : test.batch.item.MemberFieldSetMapper.java

import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;

public class MemberFieldSetMapper implements FieldSetMapper<Member> {
  @Override
  public Member mapFieldSet(FieldSet fs) throws BindException {
     Member member = new Member();
     member.setNum(fs.readInt("num", 0));
     member.setName(fs.readString("name"));
     return member;
  }
}

POJO 에의 매핑은 FieldSetMapper를 구현한 클래스에서 수행된다.

fs.readInt ("num", 0); // 두번째 인수는 취득값이 널인 경우 초기값으로 지정

fs.readDate("birthdate", null, "yyyy/MM/dd" );

5. 실행

가. 읽을 파일 c:/test/data.csv

1, 철수 2, 영희

나. 실행방법1
코맨드라인에서 다음과 같이 실행

java org.springframework.batch.core.launch.support.CommandLineJobRunner
     classpath:/example/launch-context.xml job1

다. 실행방법2

이클립스에서

라. 실행결과

    철수, 1
    영희, 2

 

최종적으로 POJO에 값을 매핑하는 용으로 BeanWrapperFieldSetMapper 클래스 라는 걸 쓸수도 있다.

POJO 대신에 LinkedHashMap 를 사용할 수 있다.

728x90
반응형