배치 3.0.4 개발 가이드
배치 실행 어플리케이션 설치
개요
배치 실행 어플리케이션이란 개발된 배치 응용 프로그램을 스케줄링하여 실행이 가능하도록 해주는 어플리케이션으로 배치 실행 어플리케이션에서 각 개별 배치 응용 프로그램을 등록, 스케줄링하여 Process Fork 하는방식으로 실행하고 실행이력을 저장하는 기능을 수행하는 Stand Alone 어플리케이션이다. 배치 3.0.4은 배치 작업 성능 및 시스템 안정성 향상을 위해 어드민(관리) 서버와 워크플로우 실행 서버를 분리한 아키텍처를 적용한다.
![20231130001 20231130001](images/20231130001.png)
![distribution_architecture distribution_architecture](images/distribution_architecture.png)
No | 구분 | Description |
---|---|---|
1 | 어드민 서버(관리 노드) | 캐모마일 배치 작업 관련 전반의 시스템과 배치 작업을 수행할 실행 노드 관리 |
2 | 실행 서버(실행 노드) | 실제 배치 워크플로우 및 Job 실행 |
이번 가이드를 통해서 배치 실행 어플리케이션을 설치할 수 있다. 설치 이후에, 파일 압축 Job을 수행하는 배치 워크플로우를 생성하고, 실행하는 과정을 통해서 간단하게 배치 실행 어플리케이션을 사용해 볼 수 있다.
사전 준비
다음은 캐모마일 배포 파일(Chamomile.zip)이 C드라이브 Chamomile 폴더에 배포된 윈도우 컴퓨터에서 실행할 떄 를 설명한다.
MySQL(MariaDB)는 설치되어 있다고 가정한다.
Java 8 버전을 설치한다.
데이터베이스 계정 생성
데이터베이스를 생성한다.
데이터베이스 이름: chamomile
어플리케이션 설치 및 데이터베이스 설정
Chamomile.zip 파일을 C드라이브에 압축 해제한다.
압축 해제 후 생성된 Chamomile/chamomile-batch-
3.0.4
-RELEASE/conf 폴더에 위치한 application.properties파일에서 datasource 설정 정보를 수정한다.
데이터베이스 테이블 생성
Chamomile/chamomile-batch-3.0.4-RELEASE/sql/ddl 폴더에 위치한 chamomile-batch.mysql.sql 파일을
참고해서 테이블을 생성한다.
이때, Chamomile online을 설치한 이력이 없어 DB에 [CHMM_USER_INFO, CHMM_USER_ROLE_MAP, CHMM_USER_PREVIOUS_PASSWORDS, CHMM_USER_INFO_LOG, CHMM_ERROR_LOGGING, CHMM_USER_LOGIN_LOG] 테이블이 존재하지 않는 경우, chamomile-batch.mysql.sql 파일을 참고해서, 해당 부분 주석을 해제하고 테이블을 생성한다. 배치 3.0.4에서는 사용자 관리에 관한 [CHMM_USER_INFO, CHMM_USER_ROLE_MAP_LOG] 테이블에 몇 가지 필드가 추가되었다.
배치 3.0.4 에서는 분산 배치 실행에 관한 [CHMM_BAT_EXEC_NODE_INFO, CHMM_BAT_CATE_EXEC_NODE_INFO] 두 테이블을 추가로 생성해줘야 한다.
기본 계정 정보를 테이블에 추가한다. (ID: admin, password: 1111)
아래 sql은 Chamomile/chamomile-batch-3.0.4-RELEASE/sql/data/chamomile-batch.data.mysql.sql
에서 확인할 수 있다.
Chamomile/chamomile-batch-3.0.4-RELEASE/sql/spring-batch 폴더에 위치한 spring-batch.mysql.sql 파일을 참고해서 테이블을 생성한다.
라이선스 적용
제공받은 라이선스 파일을 chamomile-batch-3.0.4/conf
폴더에 위치시킨다.
배치 실행 어플리케이션 실행
Chamomile/chamomile-batch-3.0.4-RELEASE 폴더에 위치한 application.bat
파일을 실행한다.
[https://[설치서버호스트]:설정포트(https://설치서버호스트:설정포트)
application.xml에서 포트정보를 변경하지 않았다면 https://localhost:6100으로 웹콘솔에 접속한다.
![20231123143007.png 20231123143007.png](images/20231123143007.png)
로그인 화면이 정상적으로 열린다면 로그인한다.
Default 사용자/패스워드는 ADMIN / 1111 이다.
배치 워크플로우 관리
좌측 메뉴에서, 배치워크플로우 관리
를 선택하여 메뉴에 진입할 수 있다.
배치 워크플로우 등록
![20231123143103.png 20231123143103.png](images/20231123143103.png)
+
버튼을 클릭하여 새로운 배치 워크플로우를 생성한다.
![20231123143131.png 20231123143131.png](images/20231123143131.png)
#
표시된 항목은 필수 입력 값이므로 모두 입력한다.
Job 생성
하나의 배치 워크플로우는 여러 개의 Job으로 구성될 수 있다. 이번 예시에서는 파일을 압축하는 Job을 생성한다.
![Job 생성 화면 Job 생성 화면](images/c5a6bc9a-689c-4fc4-a0ad-16a317cf9fec.png)
+
버튼을 클릭하여 Job을 생성한다.
다음으로 압축할 파일을 준비한다.
Chamomile/chamomile-batch-3.0.4-RELEASE 폴더에 compress라는 이름으로 폴더를 생성한다.
compress 폴더 내에 inputFile.txt 파일을 생성한다.
inputFile.txt 파일의 경로는 Chamomile/chamomile-batch-3.0.4-RELEASE/compress/inputFile.txt 이다.
![Job 상세 내역 화면 Job 상세 내역 화면](images/2895bda6-8fa6-4651-a483-a7a46653392e.png)
생성된 Job(회색 사각형)을 클릭한 이후에, 위와 같이 입력해준다.
입력이 끝나면, 상단에 저장
버튼을 클릭한다.
배치 워크플로우 실행
배치 워크플로우 관리
메뉴로 이동하여 위에서 생성한 워크플로우를 조회한다.
그리고 실행
버튼을 클릭하여 배치워크플로우를 실행한다.
압축된 결과 파일을 확인한다.
Chamomile/chamomile-batch-3.0.4-RELEASE/compress 폴더로 이동하여 outputFile.tar.gz 파일을 확인한다.
실행 이력 조회
배치워크플로우 상세 내역의 하단에서 워크플로우 실행 이력을 확인할 수 있다.
![20231123143413.png 20231123143413.png](images/20231123143413.png)
또한 실행이력 조회
메뉴에서 실행이력을 조회할 수 있다.
![20231123143455.png 20231123143455.png](images/20231123143455.png)
배치 프로젝트 생성 및 개발
DB에서 데이터를 읽어 파일로 생성하는 간단한 Job을 만들어 테스트를 진행한다.
프로젝트 생성(~2.4.0)
프로젝트 정보입력
먼저
File
->New
->Project
를 선택한 후Chamomile Framework
->Create Chamomile Project
를 선택한 후Next
를 클릭한다.아래와 같이 창이 생성되면 값을 입력해준다.
![image-20220405164821791 image-20220405164821791](images/image-20220405164821791.png)
생성할 프로젝트는 chamomile batch jobs
를 선택하고 Next
버튼을 클릭한다.
Database정보 입력
생성될 프로젝트의 Database정보를 입력해야 한다. 미리 설정해 둔 Datasource의 목록이 표시되며 콤보박스에서
root
를 선택하고Connection test..
를 클릭하여 정상적으로 연결이 되는지 확인한다. 이후Finish
버튼으로 프로젝트 생성을 완료한다.
프로젝트 생성(3.0.4)
캐모마일 IDE가 3.0으로 버전업되면서 프로젝트 생성 절차가 새로운 방식으로 변경되었습니다. 아래 가이드를 참고하여 새로운 캐모마일 프로젝트를 생성해주세요.
필수 체크 사항 1.설치 경로: IDE 압축 파일은 C 드라이브에서 압축풀어주세요. : 최종경로 C:\chamomile 2.maven 설정: Preferences > User Settings
![20231012150312 20231012150312](images/20231012150312.png)
User Settings 설정에서 Global Settings에 C:\Chamomile\maven\settings.xml 경로를 설정한 후 Update Settings
버튼을 클릭합니다. 다음 Apply
버튼을 클릭합니다.
1. File > New > Maven Project
![20231011161040 20231011161040](images/20231011161040.png)
2. 아래 화면에서 Next 클릭
![20231011161428 20231011161428](images/20231011161428.png)
3. Catalog Selectbox - maven-releases 선택
![20231011162557 20231011162557](images/20231011162557.png)
선택한 카탈로그(catalog)에 따라 출력되는 아티팩트(artifact) 리스트를 선택하는 절차를 설명드리겠습니다. 아티팩트 리스트가 정상적으로 표시되는 경우, 3.1과 3.2 단계는 건너뛰어도 됩니다.
단계 1: 카탈로그 선택
카탈로그를 선택하는 드롭다운(selectbox)을 찾습니다.
원하는 카탈로그를 선택합니다.
단계 2: 아티팩트 선택
선택한 카탈로그에 따라 아티팩트 리스트가 표시됩니다.
원하는 아티팩트를 선택합니다. 만약 아키타입 리스트가 나오지 않는 경우 아래 3.1, 3.2 설명을 참고해주세요.
3.1: 아티팩트 리스트가 표시되지 않는 경우
문제 해결 (아티팩트 리스트가 표시되지 않을 때)
아티팩트 리스트가 나오지 않는 경우, 카탈로그를 다시 확인하고 올바른 카탈로그를 선택했는지 확인하세요.
catalog에 maven-releases 가 없는 경우 local-archetype 선택 바랍니다.
local-archetype도 없는 경우 아래의 3.2 절차를 수행합니다.
3.2 카탈로그(catalog)에 local-archetype 없는 경우
![20231011161730 20231011161730](images/20231011161730.png)
아래와 같이 입력하세요:
Catalog File: C:\Chamomile\maven\archetype-catalog.xml (browse 버튼을 선택하여 파일을 추가하세요.) Description: local-archetype
4. Group Id, Artifact Id, Package 입력 후 Finish
![20231123175453 20231123175453](images/20231123175453.png)
프로젝트 정보입력
프로젝트가 생성되면 발급받은 라이선스 파일을 conf 폴더로 복사한다.
![image-20220405165137173 image-20220405165137173](images/image-20220405165137173.png)
코드 생성
생성된 프로젝트 -> 마우스 오른쪽 버튼 ->
Chamomile
->generate Batch Job
->OK
![image-20220405165234952 image-20220405165234952](images/image-20220405165234952.png)
Job 생성도구에서 다양한 배치 유형에 대해 코드를 생성할 수 있는 기능을 제공하고 있으며, 이번 예시에서는
DB to File
을 선택하여 만들어 보도록 한다.배치 유형: DB to File
VO 생성 방식: 테이블에서 직접 생성
Job name: exportBoard
Package name: net.lotte.sample.board
VO 정보 입력을 위해
스키마(chamomile)
와테이블(demo_board)
을 선택한 후Next
Job에서 동작하는 Reader는
Jdbc
,Stored Procedure
,MyBatis
를 이용하여 처리될 수 있으며, Writer 는Delimited
,Formatter
,Json
,XML
형식을 지원하며, 아래와 같이 입력 후Finish
를 선택한다.Reader: JdbcCursor ItemReader
Reader Query: select * from demo_board
Writer: Json ItemWriter
테스트
소스코드 생성 시 배치 Job을 손쉽게 테스트 하기 위한 Junit 테스트 코드를 자동으로 생성한다.
src/test/java 폴더 내 net.lotte.sample.board.exportBoardTest를 선택하여 테스트를 수행한다.
테스트는 소스코드에서 우클릭 > Run As > JUnit Test를 선택하여 실행한다.
![image-20220405171308079 image-20220405171308079](images/image-20220405171308079.png)
정상적으로 실행이 완료된 경우 demo_board
테이블의 정보가 명시 된 output
파라매터의 값에 따라 파일로 생성된 것을 확인할 수 있다.
서버로 배포
배치 job을 서버로 배포하기 위한 방법은 아래와 같이 2가지 방법을 제공한다.
class 파일 단위로 개별 배포
jar로 생성하여 배포 (추천)
프로젝트에서 우클릭
> Run As
> 5 Maven Build...
를 선택 하여 생성 된 위자드에서 Goals
를 clean package
를 입력한 후 실행한다.
![image-20220405172215114 image-20220405172215114](images/image-20220405172043753.png)
![image-20220405172215114 image-20220405172215114](images/image-20220405172215114.png)
아래와 같이 빌드 로그가 발생하고, 프로젝트의 target/edu-batch-1.0.0.jar 파일이 생성된 것을 확인한다.
배치 실행 어플리케이션이 설치 된 디렉토리로 이동하여 deploy 폴더를 생성하고 jar 파일을 복사한다.
결과는 아래와 같다.
![image-20220405172723930 image-20220405172723930](images/image-20220405172723930.png)
워크플로우 등록 및 실행
이제 배포 된 job을 이용하여 워크플로우를 만들어 실행해 보자.
![image-20231123144039 image-20231123144039](images/20231123144039.png)
+
버튼을 클릭하여 새로운 배치 워크플로우를 생성한다.
![image-20231123144101 image-20231123144101](images/20231123144101.png)
#
표시된 항목은 필수 입력 값이므로 모두 입력한다.
+
버튼을 클릭하여 Job을 생성한다.
![create_job_heapsize create_job_heapsize](images/create_job_heapsize.png)
생성된 Job(회색 사각형)을 클릭한 이후에, 위와 같이 입력해준다.
입력이 끝나면, 상단에 저장
버튼을 클릭한다.
해당 워크플로우를 실행하여 결과를 확인한다.
아래와 같이 exportedBoard.json 파일에 생성 된 데이터를 확인할 수 있다.
![image-20220405174923215 image-20220405174923215](images/image-20220405174923215.png)
실행 노드 생성 및 관리
캐모마일 분산 배치는 카테고리 종류 별로 실행 노드를 지정할 수 있다. 따라서 배치 워크플로우를 수행할 실행 노드(서버)를 생성 및 관리하고 싶다면 워크플로우 생성 시, 특정 카테고리를 지정하고 해당 카테고리에 매핑할 실행 노드를 생성하면 된다.
단계 1. 실행 노드 생성
특정 카테고리를 전용으로 실행할 노드(서버)를 생성한다. 실행 노드 관리는 [환경 설정 > 실행 노드 관리] 페이지에서 할 수 있다. 해당 페이지에서 실행 노드 정보를 등록/수정/삭제/서버 상태 체크 등의 작업을 할 수 있다.
![manage_node manage_node](images/manage_node.png)
단계 2. 카테고리 생성 및 수정
실행 노드 정보와 매핑할 카테고리를 생성한다. (이미 존재하는 카테고리에 실행 노드 정보를 매핑하고 싶은 경우에는 수정하면 된다.) 이 작업은 배치 워크플로우 관리 페이지에서 트리 구조로 되어있는 카테고리 목록의 '+' 버튼을 누르면 된다.
![make_category make_category](images/make_category.png)
단계 3. 워크플로우 카테고리 지정
배치 작업을 수행할 워크플로우를 생성 시 특정 실행 노드에 매핑 될 카테고리를 지정한다. 만약 아무 카테고리를 지정하지 않으면 어드민(관리 노드)에서 로드밸런싱을 하여 적절한 실행 노드를 찾아 배치 작업을 수행한다.
배치 개발 가이드
File DB 공통 개발가이드
배치 개발 절차
절차
배치 업무 개발은 Spring Batch를 기반으로 한다.
배치 개발 Flow
기능 정의 (전문 정의)
배치설계서를 통해 필요한 기능을 도출한다.
배치 유형을 정의한다. (Tasklet, Job-Step)
Input, Output을 정의한다. (전문정의)
선후행 작업을 정의한다.
XML 작성
Job과 Step을 정의하고 관계를 작성한다.
기능정의를 통해 설계한 내용을 바탕으로 XML을 작성한다.
Java Class 작성
Job을 구성하는 Step의 Class를 작성한다.
JUnit 단위 테스트
Eclipse Plug-In에서 배치작업에 대한 단위테스트 코드를 생성하고 테스트를 진행한다.
Junit Test Class 생성 및 단위 테스트를 진행한다.
![image image](images/202311300004.png)
배치 개발 가이드
배치 코딩 가이드 > FILE, DB 공통
배치 JAVA 기본 구조
아래와 같이 기본 구조를 생성한다. (File과 DB 모두 Java의 형태는 itemReader, itemProcessor, itemWriter로 구성된다.)
VO는 같은 Java파일에 구성해도 되고, VO만을 담은 별도의 Java파일로 분리해도 된다.
배치 XML 기본 구조
Job과 Step을 정의한 XML을 작성한다. (아래의 소스는 FILE에 대한 소스이며, DB 또한, FILE과 형태는 동일하다.)
배치 VO클래스 작성
Mapping 하기 위한 VO Class를 작성한다.
배치 FILE 처리 유형
ItemReader
Delimited
FixedLength
Json
XML
ItemProcessor
ItemProcessor
xmlItemProcessor
ItemWriter
Delimited
FixedLength
Json
XML
**배치 코딩 가이드 > FILE **
ItemReader
1) Delimited File Reader
구분자로 컬럼이 분리된 파일 형식을 읽을 때 사용한다.
2) FixedLength File Reader
고정 컬럼 길이 파일 형식을 읽을 때 사용한다.
3) Json File Reader
JSON 파일 형식을 읽을 때 사용한다.
각 라인에 JSON 객체가 들어간다. (JSON ARRAY 일 지라도 한 라인에 들어가 있으면 하나의 레코드로 인식한다.)
4) XML File Reader
XML 파일 형식을 읽을 때 사용한다.
ItemProcessor
1) ItemProcessor
Reader에서 가져온 레코드를 처리한다.
2) ItemProcessor (XML 형식)
itemReader에서 추출한 레코드를 처리한다.
(XML 파일 형식에 대한 itemReader)
ItemWriter 1) Delimited File Writer
구분자로 분리된 형식의 파일을 출력한다.
2) Formatter File Writer
정의된 포맷으로 파일을 출력한다.
3) Json File Writer
JSON 형식으로 파일을 출력한다.
4) XML File Writer
XML 형식으로 파일을 출력한다.
5) XML File Writer (입력이 XML 형식의 Record인 경우 사용)
XML 형식으로 파일을 출력한다.
배치 DB 처리 유형
ItemReader
JdbcCursorItemReader
StoredProcedureItemReader
JdbcPagingItemReader
MybatisCursorItemReader
MybatisPagingItemReader
ItemProcessor
ItemProcessor
ItemWriter
JdbcBatchItemWriter
JdbcXmlItemWriter
MybatisBatchItemWriter
배치 코딩 가이드 > DB
ItemReader
1) JdbcCursorItemReader
Jdbc Cursor로 Query를 실행하여 Record를 가져온다.
2) StoredProcedureItemReader
Stored Procedure를 실행하여 Record를 가져온다.
3) JdbcPagingItemReader
4) MyBatisCursorItemReader
5) MyBatisPagingItemReader
ItemWriter
1) JdbcBatchItemWriter
2) JdbcXmlItemWriter
Jdbc를 사용하여 Database에 Insert 한다.
Reader가 XML 형식일 때 Target을 해당 형식으로 선언하여 사용한다.
3) MyBatisBatchItemWriter
Junit 테스트 가이드
배치업무는 단위 테스트를 위해 Junit 테스트 방법을 제공한다. 이는 개발된 업무 배치 만을 단위 테스트 한다.
Junit Test Class 생성
<모듈명>
Test Class를 아래와 같이 작성한다.
JUnit Test 실행 (Run)
앞의 과정이 완료되면 각 업무배치 별 JUnit Test가 가능하다.
Test를 위한 방법은 다음과 같다.
STEP 1.
Navigation : 업무 배치 Class -> 마우스 오른쪽 클릭 -> Run As -> JUnit Test 선택
STEP 2.
해당 JUnit Adapter Class가 최초 실행됬을 경우 이클립스 설정에 따라 Launcher 선택화면이 나타날 수 있다. Launcher 화면에서 “Eclipse JUnit Launcher” 를 선택하고 “OK”
![image-20201103154721682 image-20201103154721682](images/image-20201103154721682.png)
JUnit Test 로그 확인
JUnit Test가 실행되면 Eclipse의 Console창에 로그가 출력된다.
로그의 형태는 로그 세팅과 Adpater 로그에 따라 다소 차이가 있을 수 있다.
![image-20201103154827312 image-20201103154827312](images/image-20201103154827312.png)
Mybatis_개발가이드
구성도
![image image](images/202311300005.png)
배치 코딩 가이드 > Mybatis
VO 작성
DB 컬럼에 대한 VO(Value Object)를 작성한다.
DAO에서 작성된 property명과 일치해야 한다.
변수 정의와 함께 Getter와 Setter 매소드를 정의해야 한다.
package net.lotte.chamomile.batch.example.spring.mybatis; public class MybatisVO { private String id; private String name; private String email; private String inout; private String create_dt; public String getUserNm() { return userNm; } public void setUserNm(String userNm) { this.userNm = userNm; } //중략.. }DAO 작성 (SQL Query.. .xml)
SQL 쿼리문을 통해 데이터 Fetch, Insert가 가능한 부분이다.
경로를 적는 부분(namespace, type 등)에 올바른 경로가 들어갈 수 있도록 한다.
각 쿼리(select, insert)별 ID는 Reader, Processor, Writer정의된 java에서 매핑하여 사용하는 부분이므로 타 사용자가 보기에도 명확하게 이해할 수 있도록 ID를 정한다.
각각의 DB별로 쿼리문이 상이하오니 쿼리문 작성에 유의하여야 한다.
VO를 통해 특정 값을 삽입할 경우, “#{}”를 이용해 값을 넣을 수 있다.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.lotte.chamomile.batch.example.spring.mybatis.MybatisDao"> <resultMap id="resultUsers" type="net.lotte.chamomile.batch.example.spring. mybatis.MybatisVO"> <result column="USER_NM" property="userNm"/> <result column="USER_PW" property="userPw"/> <result column="ENABLED" property="enabled"/> </resultMap> <select id="selectUsers" resultMap="resultUsers"> SELECT USER_NM AS USER_NM ,USER_PW AS USER_PW ,ENABLED AS ENABLED FROM TEST.DBO.USERS </select> <insert id="insertUsers"> INSERT INTO TEST.DBO.USERS VALUES (#{userNm}, #{userPw}, #{enabled}) </insert> </mapper>JOB, STEP 정의 XML 작성
JOB, STEP을 정의하고, Reader, Processor, Writer를 정의한다. (JOB과 STEP의 순서 정의와 배치 트랜잭션에 대한 설정을 할 수 있다.)
Spring Batch의 기본 기능인 트랜잭션 설정이 필요한 경우, 추가로 설정한다. (commit-interval, next 등)
<beans:beans xmlns="http://www.springframework.org/schema/batch" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.2. xsd"> <context:component-scan base-package="net.lotte.chamomile.batch. example.spring.mybatis"/> <job id="personJob" xmlns ="http://www.springframework.org/schema/batch"> <step id="step1"> <tasklet transaction-manager="transactionManager"> <chunk reader="personReader" processor="personProcessor“ writer="personWriter" commit-interval="2"/> </tasklet> </step> </job> </beans:beans>ItemReader, ItemProcessor, ItemWriter 정의 Java 작성
SqlSessionFactory
한 DB에서 읽고, 다른 DB에서 쓰는 즉, 2개 이상의 DB에서의 동작을 위해서는 Spring에서 제공하는 기능인 @Autowired를 통해 특정 DB로의 접근이 가능하다. (SqlSessionFactory에 대한 설명은 3.2.1을 참고한다.)
특정 DB의 접근을 위해 java 상단에 아래와 같이 사용할 각각의 DB를 정의한다.
@Autowired private SqlSessionFactory oracle; @Autowired private SqlSessionFactory mysql; @Autowired Private SqlSessionFactory mssql;ItemReader
setSqlSessionFactory를 통해 특정 DB의 Connection이 가능하다. (setSqlSessionFactory의 값은 DB의 이름만 넣으면 가능하도록 SqlSessionFactory를 정의한다.)
SQL Query문이 들어가 있는 XML의 Query ID와 Mapping하여 특정 쿼리문을 실행시킬 수 있다.
@Bean ItemReader<MybatisVO> personReader() throws Exception { MyBatisCursorItemReader<MybatisVO> itemReader = new MyBatisCursorItemReader<MybatisVO>(); //MSSQL itemReader.setSqlSessionFactory(mssql); itemReader.setQueryId("selectUsers"); return itemReader; }
배치 설정 가이드 > Mybatis
Mybatis 설정(pom.xml)
Mybatis 사용 Spring과 Mybatis 연결, 특정 DB와의 연결을 위해 아래와 같이 프로젝트 하위의 POM.xml에 Dependency 설정을 한다.
DB Properties 설정
Datasource의 value값을 Mapping시키기 위해 별도의 DB Properties 파일로 분리하였다.
기본적인 DB Properties 파일들은 다음과 같다. (특정 DB에 대한 Url, User, Password등 정보는 각 프로젝트의 상황에 맞춰 작성하면 된다.)
DB Connection 설정
DB Connection 시 사용하게 될 Connection 정보를 작성
각 DB별 Connection 설정은
{oracle.~}과 같이 사용하면, 각 DB별로 설정된다. ref는 각 DB별로 mssqlDatasource, mysqlDatasource 등으로 정의
SqlSession을 관리하기 위한 SqlSessionFactory를 작성
Junit 테스트 가이드
배치업무는 단위 테스트를 위해 Junit 테스트 방법을 제공한다. 이는 개발된 업무 배치 만을 단위 테스트 한다.
Junit Test Class 생성
<모듈명>.java 생성 : 테스트에서 사용할 Class를
${BATCH_HOME}/src/test/{Class Path}/<모듈명>
에 생성한다.Test Class를 아래와 같이 작성한다.
JUnit Test 실행 (Run)
앞의 과정이 완료되면 각 업무배치 별 JUnit Test가 가능하다.
Test를 위한 방법은 다음과 같다.
STEP 1.
Navigation : 업무 배치 Class -> 마우스 오른쪽 클릭 -> Run As -> JUnit Test 선택
STEP 2.
해당 JUnit Adapter Class가 최초 실행됬을 경우 이클립스 설정에 따라 Launcher 선택화면이 나타날 수 있다. Launcher 화면에서 “Eclipse JUnit Launcher” 를 선택하고 “OK”
![image-20201103154721682 image-20201103154721682](images/image-20201103154721682.png)
JUnit Test 로그 확인
JUnit Test가 실행되면 Eclipse의 Console창에 로그가 출력된다.
로그의 형태는 로그 세팅과 Adpater 로그에 따라 다소 차이가 있을 수 있다.
![image-20201103154827312 image-20201103154827312](images/image-20201103154827312.png)
에러 처리_개발가이드
배치 코딩가이드 > 에러처리
구성도
![image image](images/202311300006.png)
XML 생성
Job과 Step을 정의한 XML을 작성
모듈에서 사용할 xml을
${BATCH_HOME}/src/resources/jobs
에 작성한다.Exception을 위한 별도 XML 설정은 없으며, Class에서 처리한다.
에러 처리 (Exception)
<모듈명>
Exception 처리 작성 : 배치 업무 Logic 안에서 Exception 처리를 작성한다.
try – catch 로 발생 가능한 Exception을 명시해 준다. 명시하지 않을 경우 발생한 Exception은 바로 상위로 전달된다.
반드시 catch한 Exception을 throw하여 상위로 보낸다. 상위로 전달하지 않으면 Exception에 따른 배치 에러처리가 되지 않는다.
에러 처리 (Exit Code Mapper)
배치에서 제공하는 Exit Code Mapper 사용법은 다음과 같다.
배치처리에서 정상종료는 0, 실패는 1로 Exit Code가 설정된다.
(Command Line에서 실행 시) 이를 사용자 요청에 따라 Mapping 할 수 있다.
<모듈명>
에러 처리 (Exit Code Mapper)
<모듈명>
ExitCodeMapper 처리 작성: 대상 Class에서 ExitCodeMapper를 implements한다.
에러 처리 (Error Skip)
Exception 발생 시 종류 및 건수에 따라 Skip 처리할 수 있다.
<모듈명>
에러 처리 (No Rollback)
일반적으로 (skip이나 retry가 아닐 경우) ItemWriter에서 오류가 발생 시 Rollback 처리된다.
no-rollback-exception-classes 명시를 통하여 특정 Exception에서 Rollback처리를 피할 수 있다.
<모듈명>
에러 처리 (Step End)
end Element를 통하여 여러 Step으로 구성된 Job의 한 Step이 FAILED 처리 되었을 경우에도 Job은 COMPLETED 처리가 가능하다.
<모듈명>
에러 처리 (Step Fail)
fail Element를 통하여 여러 Step으로 구성된 Job의 한 Step이 FAILED 처리 되었을 경우에 Job을 특정 exit-code로 처리할 수 있다. (Job이 FAILED처리 되는것은 같다.)
<모듈명>
에러 처리 (Step Fail)
fail Element를 통하여 여러 Step으로 구성된 Job의 한 Step이 FAILED 처리 되었을 경우에 Job을 특정 exit-code로 처리할 수 있다. (Job이 FAILED처리 되는것은 같다.)
<모듈명>
Batch Exception 소개
Spring Batch에서 발생하는 Exception의 일부를 소개한다.
JobRestartException
<job id="simpleJob" xmlns="http://www.springframework.org/schema/batch" restartable="false">Job은 기본적으로 실행 시 마다 JobInstance가 생성되어 실행되게 된다. 이러한 재실행을 방지할 필요가 있을 경우 restartable을 false로 설정할 수 있다. 그리고 이러한 Job은 재실행 시 JobRestartException을 발생하고 종료된다.
FlatFileParseException FlatFileItemReader에서 File을 읽을 때 어떠한 오류가 발생한다.
FlatFileFormatException LineTokenizer에서 tokenzing 시 오류가 발생한다.
IncorrectTokenCountException DelimitedLineTokenizer와 FixedLengthLineTokenizer에서 FieldSet의 갯수가 맞지 않을 경우 발생한다.
IncorrectLineLengthException 정의된 Range와 Line의 길이(한 행의 길이)가 같지 않을 경우에 발생한다. 단, setStrict를 통하여 오류를 넘길 수 있다.
tokenizer.setColumns(new Range[] { new Range(1, 5), new Range(6, 10) }); tokenizer.setStrict(false); FieldSet tokens = tokenizer.tokenize("12345"); assertEquals("12345", tokens.readString(0)); assertEquals("", tokens.readString(1));
Junit 테스트 가이드
배치업무는 단위 테스트를 위해 Junit 테스트 방법을 제공한다.
JUnit 테스트는 Server를 실행하지 않고 개발된 업무 배치 만을 단위 테스트 한다.
Test 배치 Class 생성
<모듈명>
TEST Class 작성: 테스트에 사용할 Class를 작성한다.
JUnit Test 실행 (Run)
앞의 과정이 완료되면 각 업무배치 별 JUnit Test가 가능하다.
Test를 위한 방법은 다음과 같다.
STEP 1.
Navigation : 업무 배치 Class -> 마우스 오른쪽 클릭 -> Run As -> JUnit Test 선택
STEP 2.
해당 JUnit Adapter Class가 최초 실행됬을 경우 이클립스 설정에 따라 Launcher 선택화면이 나타날 수 있다. Launcher 화면에서 “Eclipse JUnit Launcher” 를 선택하고 “OK”
![image-20201103154721682 image-20201103154721682](images/image-20201103154721682.png)
JUnit Test 결과 확인
JUnit Test가 실행되면 Eclipse의 Console창에 로그가 출력된다.
로그의 형태는 로그 세팅과 Adpater 로그에 따라 다소 차이가 있을 수 있다.
![image-20201103154827312 image-20201103154827312](images/202311300007.png)
트랜잭션_개발가이드
구성도
![image image](images/202311300008.png)
transaction-manager : 배치 처리 중 트랜잭션의 시작과 커밋을 담당하는 역할이다.
commit-interval : 커밋할 아이템의 수이다.
Exception 발생 시 Rollback Transaction을 통해 Read된 Item이 Rollback 처리된다.
각 아이템 처리 중 Skippable Exception 발생 시 Read된 Item이 삭제된 후 트랜잭션의 처음 상태로 이동한다.
설정 가이드
Job Configuration
Restartablility에 대한 XML 작성
“restartable (true / false)” 옵션을 사용하여 적용한다. restartable : 스프링 배치가 job를 실행할 수 있는지 여부 지정 옵션(Default : true)
true : Job을 1회 이상 실행 가능하다.
false : Job을 1회 이상 실행 불가하다. (1회 이상 실행 시, JobRestartException 발생)
Job, Step, tasklet, chunk 간 포함 관계
Job > Step > Tasklet > Chunk
Step Configuration
Commit Interval
Commit Interval 테스트를 위한 XML
Listener.xml 을 이용해서 commit-interval 옵션에 대해 알아본다.
Commit Interval 결과
Read Listener가 2개, 혹은 3개로 달라지는 것을 볼 수 있다. (commit-interval의 이해를 돕기 위한 부분으로 Read부분만 나타낸다.)
![image image](images/202311300009.png)
Configuring Step Restart
Configuring Step Restart에 대한 XML 작성
“allow-start-if-complete (true / false)” 옵션을 사용하여 적용한다. (Default : False) (True : Step이 성공(complete)적으로 끝난 경우에도 해당 Step에 대해 재실행이 필요한 경우이다. False : 실패한 Step에 대해서만 재실행한다. (성공한 Step은 Skip))
Controlling Rollback
ControllingRollback.java 생성
에러 발생 시 선언된 특정 에러에 대해서는 에러가 발생되지 않는다. (아래 예제는 DB To DB 처리 중 Write 시 에러를 발생시키도록 하여 Rollback 예제를 구현하였다.)
@Bean ItemWriter<Person> personWriter() throws Exception { JdbcBatchItemWriter<Person> personWriter = new JdbcBatchItemWriter<Person>(); personWriter.setDataSource(dataSource); //실제 존재하지 않는 테이블에 Write하여 에러가 발생되도록 한다. personWriter.setSql("INSERT INTO CHMM_BAT_EXAM_DBTODB_ROLLBACK(ID,NAME,EMAIL,INOUT,CREATE_DT) VALUES (:id,:name,:email,'OUT',SYSDATE)"); personWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Person>()); return personWriter; }에러 발생 시 선언된 특정 에러에 대해서는 에러가 발생되지 않는다. (아래 예제는 DB To DB 처리 중 Write 시 에러를 발생시키도록 하여 Rollback 예제를 구현하였다.)
<context:component-scan base-package="net.lotte.chamomile.batch.example.spring.transaction.rollback"/> <job id="personJob" xmlns="http://www.springframework.org/schema/batch"> <step id="step1"> <tasklet transaction-manager="transactionManager"> <chunk reader="personReader" processor="personProcessor" writer="personWriter" commit-interval="2"/> <!-- BadSqlGrammarException 발생 시 Exception을 발생시키지 않는다.--> <no-rollback-exception-classes> <include class="org.springframework.jdbc.BadSqlGrammarException"/> </no-rollback-exception-classes> </tasklet> </step> </job>ControllingRollback 테스트 결과
- Write 시 Write를 할 수 없다는 에러를 발생시켜야 하나 “no-rollback-exception-classes” 사용을 통해 에러가 발생되지 않는 것을 볼 수 있다. 아래 결과 화면은 DB의 내용에 대한 Read만 된 후 배치가 종료된 것을 확인할 수 있다.)존재하지 않는 테이블명 기재로 인한 Exception 발생
위 오류 발생 시 Exception이 발생되지 않게 “no-rollback-exception-classes” 설정 후 실행하면, 위 Exception에러가 발생되지 않는다.
Configuring Listener (ItemReadListener)
ItemReadListener 생성 각 Item별 Read와 관련하여 호출되며, Commit-Interval 기준으로 Listener가 호출된다.
Configuring Listener (StepListener)
StepListener 생성 Step과 관련하여 호출되며, Step 기준으로 Listener가 호출된다.
Configuring Listener (ItemWriterListener)
ItemWriterListener 생성 각 Item별 Write와 관련하여 호출되며, Commit-Interval 기준으로 Listener가 호출된다.
Listener 테스트를 위한 XML 작성 Read, Write, Step 관련 Listener를 테스트 하기 위하여 Listener.xml을 작성한다.
Listener 테스트 결과
![image-20201109092932253 image-20201109092932253](images/image-20201109092932253.png)
Controlling Step Flow (Sequential Flow)
Sequential Flow 테스트를 위한 XML 작성
Sequential Flow 테스트 결과
![image-20201109093147669 image-20201109093147669](images/image-20201109093147669.png)
Controlling Step Flow (Conditional Flow)
Conditional Flow 테스트를 위한 XML 작성
Conditional Flow 테스트 결과
Junit 테스트 가이드
배치업무는 단위 테스트를 위해 Junit 테스트 방법을 제공한다. 이는 개발된 업무 배치 만을 단위 테스트 한다.
Junit Test Class 생성
<모듈명>
Test Class를 아래와 같이 작성한다.
JUnit Test 실행 (Run)
앞의 과정이 완료되면 각 업무배치 별 JUnit Test가 가능하다.
Test를 위한 방법은 다음과 같다.
STEP 1.
Navigation : 업무 배치 Class -> 마우스 오른쪽 클릭 -> Run As -> JUnit Test 선택
STEP 2.
해당 JUnit Adapter Class가 최초 실행됬을 경우 이클립스 설정에 따라 Launcher 선택화면이 나타날 수 있다. Launcher 화면에서 “Eclipse JUnit Launcher” 를 선택하고 “OK”
![image-20201103154721682 image-20201103154721682](images/image-20201103154721682.png)
JUnit Test 로그 확인
JUnit Test가 실행되면 Eclipse의 Console창에 로그가 출력된다.
로그의 형태는 로그 세팅과 Adpater 로그에 따라 다소 차이가 있을 수 있다.
![image-20201103154827312 image-20201103154827312](images/image-20201103154827312.png)
동시 실행 가능 여부 관리 기능_개발가이드
구성도
![image-20201109094202121 image-20201109094202121](images/image-20201109094202121.png)
코딩 가이드
XML 생성: Job과 Step을 정의한 XML을 작성한다.
기본구조 작성
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>상세 구조 작성: Step Class를 정의하여 명시한다. 하나의 Class로 구성된 Step일 경우 아래와 같이 component-scan을 사용한다.
<context:component-scan base-package="net.lotte.chamomile.batch. template.spring.errorhandling"/>여러 개의 Class로 구성된 Step일 경우 아래과 같이 bean을 정의한다.
<bean id="normalTask" class="net.lotte.chamomile.batch.template.spring.errorhandling.NormalTasklet"> <property name="message" value="Normal Task"/> </bean>Job을 정의한다.
<job id="errorHandlingStepFail“ xmlns="http://www.springframework.org/schema/batch">Step을 정의한다. Tasklet 구조일 경우 아래와 같이 작성한다.
<step id="step1" allow-start-if-complete="true"> <tasklet ref="normalTask" /> <next on="*" to="step2"/> </step>Chunk 구조일 경우 아래와 같이 작성한다.
<step id="step1"> <tasklet transaction-manager="transactionManager"> <chunk reader="itemReader" processor="itemProcessor" writer="itemWriter" commit-interval="2"/> </tasklet> </step>
Class 작성: XML에서 정의한 Step의 Class를 작성한다.
기본 구조 생성
package net.lotte.chamomile.batch.template.spring.filetodb; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; @Configuration public class FileToDb { /** Logger */ protected final Logger logger = LoggerFactory.getLogger(getClass()); @Autowired DataSource dataSource; }상세 구조 생성 배치 유형에 따라 상세 구조를 생성한다. (3-2 배치 기능별 가이드 참고)
Restartablility 설정
적용 시나리오 Job의 재기동 여부를 제한하고자 할 때 사용한다. 기본적으로 Spring Batch의 Job은 Job Parameter 별로 Job Instance가 생성되고. 실행 단위가 된다. 정상종료(COMPLETED)로실행된 Job Instance는 재실행 되지 않으나(Restartablility가 설정되어 있다 하더라도), 오류(FAILED)나 중지(STOP) 처리된 Job Instance는 재실행이 가능하다. 이러한 재실행을 방지하고자 할 때 restartable을 false로 설정한다. (기본은 true)
코딩 절차 해당 Job의 restartable을 false로 작성한다.
<job id="footballJob" restartable="false"> ... </job>
StartLimit 설정
적용 시나리오 Step의 실패(FAILED) 이후 재시작 가능한 횟수를 명시하고자 할 때 start-limit를 설정할 수 있다. Limit 횟수를 초과하면
org.springframework.batch.core.StartLimitExceededException
이 발생한다. 기본값은 Integer.MAX_VALUE 이다.코딩 절차 해당 Step 아래 Tasklet의 start-limit를 설정한다.
<step id="step1"> <tasklet start-limit="1"> <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/> </tasklet> </step>
Split Flow
적용 시나리오 Job을 구성하는 Step을 여러 개의 Flow(Group)으로 나눠서 구성할 수 있다. 각각의 Flow가 독립적으로 실행된다. 독립된 두개의 Step간의 구성을 하고자 할 때 사용한다. 각각의 Flow가 병렬로 실행되지는 않으며, 순차 실행된다.
단, 상위에 기술된 Flow의 작업이 실패(FAILED) 처리 되어도 하위의 Flow에 영향을 미치지 않는다.
코딩 절차 아래와 같이 Job 아래 Flow를 구성한다.
<job xmlns="http://www.springframework.org/schema/batch" id="simultaneousSplitFlow"> <split id="split1" next="step4"> <flow> <step id="step1" next="step2"> <tasklet ref="stepTask1" /> </step> <step id="step2" next="errorStep"> <tasklet ref="stepTask2" /> </step> <step id="errorStep"> <tasklet ref="errorTask" /> </step> </flow> <flow> <step id="step3"> <tasklet ref="stepTask3" /> </step> </flow> </split> <step id="step4"> <tasklet ref="stepTask4" /> </step> </job>
Multithreaded Step (single process)
적용 시나리오 chunk로 구성된 step을 multi-thread로 처리하고자 할 때 사용한다.
Reader나 Writer에는 적합하지 않고, processor를 병렬로 처리하고자 할 때 사용한다.
Single-thread로 처리속도가 나오지 않을 때 multi-thread Step을 사용하면 속도가 향상된다.
기본 thread-limit 값은 4이며, 해당 property로 조정 가능하다.
(throttle-limit) 실제 작업 수행은 task-executor가 담당하고 가장 기본적인 task-executor는
SimpleAsyncTaskExecutor이다. (7. TaskExecutor의 종류 참고)
코딩 절차
Task-executor를 정의한다.
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>Step에 task-executor를 명시한다. (필요시 throttle-limit를 설정)<job xmlns="http://www.springframework.org/schema/batch" id="simultaneousMultithreadedStep"> <step id="step1"> <tasklet transaction-manager="transactionManager" task-executor="taskExecutor" throttle-limit="4"> <chunk reader="itemReader" processor="itemProcessor" writer="itemWriter" commit-interval="4" /> </tasklet> </step> </job>Itemreader와 itemwriter에는 적합하지 않으나, 필요시 아래와 같이 구현 가능하다.
<job xmlns="http://www.springframework.org/schema/batch" id="simultaneousMultithreadedStep"> <step id="step1"> <tasklet transaction-manager="transactionManager" task-executor="taskExecutor" throttle-limit="4"> <chunk reader="itemReader" processor="itemProcessor" writer="itemWriter" commit-interval="4" /> </tasklet> </step> </job>Parallel step (single process)
적용 시나리오 plit Flow를 병렬로 처리 할 수 있다. 하나의 Job에 여러 Flow로 정의된 스텝이 있고, 상호 배타적인 관계라면, 이를 병렬로 동시에 처리하여 속도를 향상시킬 수 있다. 실제 작업 수행은 Multithreaded Step과 같이 task-executor가 담당한다.
코딩 절차 아래와 같이 Split Element에 task-executor를 설정한다.
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/> <job xmlns="http://www.springframework.org/schema/batch" id="simultaneousParallelSteps" restartable="false"> <split id="split1" task-executor="taskExecutor" next="step4"> <flow> <step id="step1" next="step2"> <tasklet ref="stepTask1" /> </step> <step id="step2"> <tasklet ref="stepTask2" /> </step> </flow> <flow> <step id="step3"> <tasklet ref="stepTask3" /> </step> </flow> </split> <step id="step4"> <tasklet ref="stepTask4" /> </step> </job>
Partitioning Step (single process or multiprocess: File to File)
적용 시나리오 다중 파일 입력이나, Partitioning 되어 있는 DB에서 Data를 가져와 사용할 때 Partitioning Step을 사용하면 속도를 향상시킬 수 있다. Multithreaded Step의 throttle-limit와 유사하게 grid-size로 병렬 처리 크기를 제어할 수 있다. 동작 방식은 Partition Step(master step)이 grid-size 만큼의 slave Step을 생성하여 병렬처리한다. Partition Step은 slave step이 종료될 때 까지 대기하고, 각 결과를 취합하여 처리한다.
코딩 절차 Partition Step을 구성하고, grid-size를 설정한다.
<job xmlns="http://www.springframework.org/schema/batch" id="SimultaneousPartitioning"> <step id="step"> <partition step="step1" partitioner="partitioner"> <handler grid-size="2" task-executor="taskExecutor" /> </partition> </step> </job>Partitioner를 정의하고, 대상 resource를 명시한다.
<bean id="partitioner" class="org.springframework.batch.core.partition.support.MultiResourcePartitioner"> <property name="resources" value="classpath:data/iosample/input/delimited*.csv" /> </bean>Task Executor를 정의한다.
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>대상 Step을 작성한다.
<step xmlns="http://www.springframework.org/schema/batch" id="step1"> <tasklet transaction-manager="transactionManager"> <chunk writer="itemWriter" reader="itemReader" processor="itemProcessor" commit-interval="5" /> <listeners> <listener ref="fileNameListener" /> </listeners> </tasklet> </step>전체 구조는 아래와 같다
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <job xmlns="http://www.springframework.org/schema/batch" id="SimultaneousPartitioning"> <step id="step"> <partition step="step1" partitioner="partitioner"> <handler grid-size="2" task-executor="taskExecutor" /> </partition> </step> </job> <bean id="partitioner" class="org.springframework.batch.core.partition.support.MultiResourcePartitioner"> <property name="resources" value="classpath:data/iosample/input/delimited*.csv" /> </bean> <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" /> <step xmlns="http://www.springframework.org/schema/batch" id="step1"> <tasklet transaction-manager="transactionManager"> <chunk writer="itemWriter" reader="itemReader" processor="itemProcessor" commit-interval="5" /> <listeners> <listener ref="fileNameListener" /> </listeners> </tasklet> </step> <bean id="fileNameListener" class="net.lotte.chamomile.batch.common.OutputFileListener" scope="step"> <property name="path" value="file:./build/output/file/" /> </bean> <bean id="itemReader" scope="step" autowire-candidate="false" parent="itemReaderParent"> <property name="resource" value="#{stepExecutionContext[fileName]}" /> </bean> <!-- <bean id="inputTestReader" class="org.springframework.batch.item.file.MultiResourceItemReader"> <property name="resources" value="classpath:data/iosample/input/delimited*.csv" /> <property name="delegate" ref="testItemReader" /> </bean> <bean id="outputTestReader" class="org.springframework.batch.item.file.MultiResourceItemReader" scope="prototype"> <property name="resources" value="file:build/output/file/delimited*.csv" /> <property name="delegate" ref="testItemReader" /> </bean> <bean id="testItemReader" parent="itemReaderParent" />--> <bean id="itemReaderParent" class="org.springframework.batch.item.file.FlatFileItemReader" abstract="true"> <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="delimiter" value="," /> <property name="names" value="name,credit" /> </bean> </property> <property name="fieldSetMapper"> <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"> <property name="targetType" value="net.lotte.chamomile.batch.domain.trade.CustomerCredit" /> </bean> </property> </bean> </bean> <bean id="itemProcessor" class="net.lotte.chamomile.batch.domain.trade.internal.CustomerCreditIncreaseProcessor" /> <bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step"> <property name="resource" value="#{stepExecutionContext[outputFile]}" /> <property name="lineAggregator"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator"> <property name="delimiter" value="," /> <property name="fieldExtractor"> <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor"> <property name="names" value="name,credit" /> </bean> </property> </bean> </property> </bean> </beans>
Partitioning Step 2 (single process or multiprocess: DB to File)
적용 시나리오 입력이 DB일 때, File to File과 동일한 경우에 사용한다.
코딩 절차 File to File가 동일한 절차로 진행한다. 전체 소스는 아래와 같다.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <job xmlns="http://www.springframework.org/schema/batch" id="simultaneousPartitionJdbcJob"> <step id="step"> <partition step="step1" partitioner="partitioner"> <handler grid-size="${batch.grid.size}" task-executor="taskExecutor" /> </partition> </step> </job> <bean id="partitioner" class="net.lotte.chamomile.batch.common.ColumnRangePartitioner"> <property name="dataSource" ref="dataSource" /> <property name="table" value="EMP" /> <property name="column" value="EMPNO" /> </bean> <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" /> <step xmlns="http://www.springframework.org/schema/batch" id="step1"> <chunk writer="itemWriter" reader="itemReader" processor="itemProcessor" commit-interval="5" /> <listeners> <listener ref="fileNameListener" /> </listeners> </tasklet> </step> <bean id="fileNameListener" class="net.lotte.chamomile.batch.common.OutputFileListener" scope="step"> <property name="path" value="file:./build/output/jdbc/" /> </bean> <bean id="itemReader" scope="step" autowire-candidate="false" class="org.springframework.batch.item.database.JdbcPagingItemReader"> <property name="dataSource" ref="dataSource" /> <property name="rowMapper"> <bean class="net.lotte.chamomile.batch.domain.trade.internal.EmpSalRowMapper" /> </property> <property name="queryProvider"> <bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="fromClause" value="ORACLE.EMP" /> <property name="selectClause" value="EMPNO,ENAME,SAL" /> <property name="sortKeys"> <map> <entry key="EMPNO" value="ASCENDING" /> </map> </property> <property name="whereClause" value="EMPNO >= :minEmpno and EMPNO <= :maxEmpno" /> </bean> </property> <property name="parameterValues"> <map> <entry key="minEmpno" value="#{stepExecutionContext[minValue]}" /> <entry key="maxEmpno" value="#{stepExecutionContext[maxValue]}" /> </map> </property> </bean> <bean id="outputTestReader" class="org.springframework.batch.item.file.MultiResourceItemReader" scope="prototype"> <property name="resources" value="file:build/output/jdbc/*.csv" /> <property name="delegate" ref="testItemReader" /> </bean> <bean id="testItemReader" class="org.springframework.batch.item.file.FlatFileItemReader"> <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="delimiter" value="," /> <property name="names" value="empno,ename,sal" /> </bean> </property> <property name="fieldSetMapper"> <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"> <property name="targetType" value="net.lotte.chamomile.batch.domain.trade.EmpSal" /> </bean> </property> </bean> </property> </bean> <bean id="inputTestReader" class="org.springframework.batch.item.database.JdbcCursorItemReader"> <property name="sql"> <value><![CDATA[select EMPNO,ENAME,SAL from EMP]]></value> </property> <property name="dataSource" ref="dataSource" /> <property name="verifyCursorPosition" value="${batch.verify.cursor.position}" /> <property name="rowMapper"> <bean class="net.lotte.chamomile.batch.domain.trade.internal.EmpSalRowMapper" /> </property> </bean> <bean id="itemProcessor" class="net.lotte.chamomile.batch.domain.trade.internal.EmpSalIncreaseProcessor" /> <bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step"> <property name="resource" value="#{stepExecutionContext[outputFile]}" /> <property name="lineAggregator"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator"> <property name="delimiter" value="," /> <property name="fieldExtractor"> <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor"> <property name="names" value="empno,ename,sal" /> </bean> </property> </bean> </property> </bean> </beans>
TaskExecutor의 종류
ConcurrentTaskExecutor 이 구현체는 Java 5 java.util.concurrent.Executor의 래퍼다. 또 다른 대안은 빈 프로퍼티로 Executor 설정 파라미터를 노출하는 ThreadPoolTaskExecutor다. ConcurrentTaskExecutor를 사용해야 하는 경우는 드물지만 ThreadPoolTaskExecutor가 원하는 만큼 안정적이지 않다면 ConcurrentTaskExecutor를 대신 사용할 수 있다.
SimpleAsyncTaskExecutor 이 구현에는 어떤 스레드도 재사용하지 않고 호출마다 새로운 스레드를 시작한다. 하지만 이 구현체는 동시접속 제한(concurrency limit)을 지원해서 제한 수가 넘어서면 빈 공간이 생길 때까지 모든 요청을 막을 것이다. 실제 풀링(pooling)을 원한다면 뒷부분을더 봐야 한다.
SimpleThreadPoolTaskExecutor 이 구현체는 실제로 Quartz SimpleThreadPool의 하위클래스로 스프링의 생명주기 콜백을 받는다 이는 Quartz와 Quartz가 아닌 컴포넌트 간에 공유해야 하는 스레드 풀이 있는 경우에 보통 사용한다.
SyncTaskExecutor 이 구현체는 호출을 비동기적으로 실행하지 않고 대신, 각 호출이 호출 스레드에 추가된다. 간단한 테스트 케이스처럼 멀티스레드가 필요하지 않은 상황에서 주로 사용한다.
ThreadPoolTaskExecutor 이 구현체는 java.util.concurrent.ThreadPoolExecutor를 구성하는 빈 프로퍼티를 노출하고 이를 TaskExecutor로 감싼다. ScheduledThreadPoolExecutor같은 고급 기능이 필요하다면 ConcurrentTaskExecutor를 대신 사용하기를 권장한다.
Junit 테스트 가이드
배치업무는 단위 테스트를 위해 Junit 테스트 방법을 제공한다. 이는 개발된 업무 배치 만을 단위 테스트 한다.
Junit Test Class 생성
<모듈명>
Test Class를 아래와 같이 작성한다.
JUnit Test 실행 (Run)
앞의 과정이 완료되면 각 업무배치 별 JUnit Test가 가능하다.
Test를 위한 방법은 다음과 같다.
STEP 1.
Navigation : 업무 배치 Class -> 마우스 오른쪽 클릭 -> Run As -> JUnit Test 선택
STEP 2.
해당 JUnit Adapter Class가 최초 실행됬을 경우 이클립스 설정에 따라 Launcher 선택화면이 나타날 수 있다. Launcher 화면에서 “Eclipse JUnit Launcher” 를 선택하고 “OK”
![image-20201103154721682 image-20201103154721682](images/image-20201103154721682.png)
JUnit Test 로그 확인
JUnit Test가 실행되면 Eclipse의 Console창에 로그가 출력된다.
로그의 형태는 로그 세팅과 Adpater 로그에 따라 다소 차이가 있을 수 있다.
![image-20201109102956564 image-20201109102956564](images/image-20201109102956564.png)