이번에 일일 집계를 만들면서 spring batch를 처음 써봤다.
인터넷에 batch에 대한 이론 자료는 많으므로 여기엔 딱히 쓰진 않겠다.
spring batch는 스케줄 등록하는 기능이 없다.
그래서 Jenkins로 시간을 설정해서 빌드하고 batch는 spring batch를 썼다.
하루동안 사람들이 쓴 글의 총 수를 TEXT 테이블에서 셀렉트 해온 후
그 데이터를 AGGT(집계) 테이블에다가 넣는 예제이다.
(1) 비지니스 로직 없이 select 후 바로 insert 하기
1. XML에 JOB을 설정한다.
<job id="aggt" incrementer="incrementer" xmlns="http://www.springframework.org/schema/batch"> <step id="txt_cnt"> -- 한 job에 스텝 여러개 추가 가능 <tasklet> <chunk reader="aggtReader" writer="aggtWriter" commit-interval="1" /> </tasklet> </step> </job> <bean id="aggtReader" class="org.springframework.batch.item.database.IbatisPagingItemReader"> <property name="queryId" value="aggt.selectTextCnt" /> -- query의 namespace+id <property name="sqlMapClient" ref="dataSqlMapClient" /> -- database 선택, select, insert 하는 곳이 서로 다른 DB일 때 설정해놓은 id를 이렇게 불러서 쓰면 된다! </bean> <bean id="aggtWriter" class="org.springframework.batch.item.database.IbatisPagingItemWriter"> <property name="queryId" value="aggt.insertTextCnt" /> <property name="sqlMapClient" ref="aggtSqlMapClient" /> </bean>
JAVA에서 아무 조작 없이 셀렉트 해온 값을 Map에 들고있다가 바로 Insert 하게 된다.
* commit-interval 은 단위인데..
만약 commit-interval이 5고, 총 100건을 돌린다 할 때
5개 단위로 가고, 중간에 실패하면 5 단위로 된 곳부터 시작한다고 함.
2. query.xml (iBatis를 쓴다)
<sqlMap namespace="aggt"> <resultMap id="textAggt" class="객체 위치"> <result property="aggtDate" column="AGGT_DATE" /> <result property="textCnt" column="TEXT_CNT" /> </resultMap> <select id="selectTextCnt" resultMap="textAggt"> /* 셀렉트 해오는 쿼리 */ </select> <insert id="insertTextCnt"> /* 위에서 셀렉트 한 값을 인서트하는 쿼리 */ </insert>
(2) 비지니스 로직 삽입
1. XML에 JOB 설정
bean 설정 대신 직접 구현한다.
<job id="aggt" incrementer="incrementer" xmlns="http://www.springframework.org/schema/batch"> <step id="txt_cnt"> <tasklet> <chunk reader="aggtReader" writer="aggtWriter" commit-interval="1" /> </tasklet> </step> </job>
때에 따라서 reader, writer 사이에 processor=”클래스명” 속성을 넣어줄 수도 있다.
reader 이후 비지니스가 필요할 때 processor에서 데이터를 가공하면 된다.
2. reader 구현
내 경우랑은 좀 달라서 테스트는 안해봤는데
다른 예제들을 찾아보면 bean에서 파라미터들을 넘겨주는 경우가 있다.
그 때 파라미터 개수만큼 reader가 호출되는 듯 하다.
그래서 전역변수를 설정해놓고 리턴할 값이 null인지를 체크해야 하며,
null이 리턴되면 reader는 끝난다.
파라미터 없는 경우는 한번불리고 끝나는거 같다.
public class AggtReader implements ItemReader<TextAggt> { ... 중략 public TextAggt read() { TextAggt textAggt = new TextAggt(); // TODO : null 체크 하여 null을 리턴하던지 writer에 쓸 객체를 리턴할 것 TextCntService result = textCntService.selectTextCnt(); return result; // 리스트를 리턴하지 말고 객체 하나를 리턴할 것 } }
3.
중간에 프로세서가 있다면 프로세서를 구현하고, 나는 프로세서를 생략하겠다.
라이터는 데이터를 받아올 때 리스트로 받는다.
처음할 때 리더에서 리스트 보냈다가 라이터에서 List> 받으려 하다가 쌩고생함..
public class AggtWriter implements ItemWriter<TextAggt> { .... public void write(List<? extends TextAggt> list) { textCntService.insertTextCnt(list); } }