2021. 4. 14. 11:19ㆍspring
가. 목표
해당 배치의 목표는 DB 테이블에서 데이터를 읽어 들여, 다른 테이블에 값을 insert 한다.
ItemReader에서 데이터를 읽어 들여, ItemWriter로 insert할 예정이다.
사실 ItemProcessor를 사용하여 데이터 변환한 후 ItemWriter로 전달하는 샘플이 실제 사용에 가까우나, 일단 생략한다.
나. 배치 처리 설정 샘플 : /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="itemReader" writer="itemWriter" />
</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="1000" />
</bean>
<!-- iBatisの設定 -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:/dao/SqlMapConfig.xml"/>
</bean>
<!-- DBのItemReaderの設定 -->
<bean id="itemReader" class="org.springframework.batch.item.database.IbatisPagingItemReader" >
<property name="sqlMapClient" ref="sqlMapClient"/>
<property name="queryId" value="selectPagingMember"/>
<property name="pageSize" value="10000"/>
</bean>
<!-- DBのItemWriterの設定 -->
<bean id="itemWriter" class="org.springframework.batch.item.database.IbatisBatchItemWriter">
<property name="sqlMapClient" ref="sqlMapClient" />
<property name="statementId" value="insertMember2" />
</bean>
</beans>
ㄱ. sqlMapClient
이 클래스는 iBatis를 사용하기 위한 클래스.
특별히 SpringBatch 전용 클래스는 아니다.
ㄴ. IbatisPagingItemReader
설정 상 1페이지 10000 레코드를 sql문으로 select 해서 추출한 10000 레코드를 메모리에 전개.
전개 후 1 레코드 씩 item으로 ItemWriter에 전달한다.
ItemWriter에는 commitInterval로 설정한 숫자만큼 List로 취득해서 처리를 수행 후 커밋한다.
이것은 통상 chunk 동작대로 이루어진다.
ItemReader은 이후 ItemWriter에 전달할 데이터 (item)이 없어지면 다음페이를 읽어 들이게 된다.
읽어들일 데이터가 없어질때까지 이런 작업이 반복된다.
ㄷ. IbatisBatchItemWriter
지정한 쿼리 ID의 SQL이 실행된다. Jdbc 배치처리가 되어지는데, 매회 prepare 되지 않고, 연속되는 SQL에 파라메터를 전달되기 때문에 처리가 빨리 수행된다.
다. IBatis 설정 파일 : /dao/SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<!-- sqlMapファイル参照する -->
<sqlMap resource="/dao/SqlMap.xml"/>
</sqlMapConfig>
라. IBatis SQL설정 파일 : /dao/SqlMap.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
<select id="selectPagingMember" resultClass="test.Member">
select id, name
from (
select
row_number() over(order by id) rn,
member.*
from member
)
where
<![CDATA[
rn > (#_page# * #_pagesize#)
and rn <= ( (#_page# + 1) * #_pagesize# )
]]>
order by id
</select>
<insert id="insertMember2" parameterClass="test.Member">
insert into member2(id, name) values(#id#, #name#)
</insert>
</sqlMap>
마. 페이징 select 문
_page, _pagesize, _skiprows
select * from member order by id asc offset #_skiprows# limit #_pagesize#
바. POJO 클래스 : test.Member.java
public class Member {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
페이징 select sql문에 대한 주의점으로 언제나 같은 순번으로 select 되도록 할 필요가 있다.
왜냐하면 이전 페이지에서 이미 처리했음에도 불구하고 다시 불려져서 재처리 되면 문제가 될수 있기 때문이다.
대개 테이블의 PK로 order by하면 되지 않을까 싶다.
또한 배치 처리 도중에 처리대상 테이블의 대상 데이터가 갱신 가능성이 있는 경우, 이것에 대한 고려도 필요하다.
예를 들어 회원정보의 등록이 언제나 일어날 가능성이 있다면, 등록일이 오래된 순으로 order by 해서 먼저 처리하는 방식을 취하면 될것 같다.
어띠됬든지 상황에 맞춰서 설계를 주의해서 할 필요가 있다.
'spring' 카테고리의 다른 글
Spring Boot 개념 (0) | 2021.04.14 |
---|---|
[퍼옴] Spring Batch 주요 개념 (0) | 2021.04.14 |
06. spring batch - 스프링 배치 처리 샘플 (chunk 처리 : 파일 읽기) (0) | 2021.04.13 |
05. Spring배치 사용준비 (0) | 2021.04.12 |
04. Spring Batch 기본개념 (ExecutionContext) (0) | 2021.04.12 |