2024.03.13 - [Spring/Batch Programming] - Spring Batch Step
지난 글까지 Step에 대해서 전반적으로 알아봤다.
이번에는 예전에 직접 구현해봤던 Item 3개에 대해서 알아보고자 한다.
ItemReader
보다시피 read() 하나의 메소드만 존재한다.
※null을 리턴하면 배치가 정상적으로 종료된다. → 정상적으로 배치가 진행되고 싶으면 reader에서 null을 리턴하면 안된다!
입력형식
- 파일(Flat, JSON, XML)
Flat : 파일을 여는거 부터 어렵다,,,, 그래서 스프링 배치는 간단하게 빌더패턴으로 할 수있도록 기능을 제공해준다!
라인을 읽어서 객체로 반환해준다.
,(콤마) 등 구분되어 있는 토큰을 가져온다. 그 이후 FieldSetMapper를 통해 나뉘어진 토큰들을 클래스로 매핑 시켜준다.
FlatFileItemReader의 여러가지 속성
Property | Type |
comments - 주석은 스킵해라(디폴트 설정 : #) | String[] |
encoding | String |
lineMapper | LineMapper |
linesToSkip - 스킵을 최초에 얼마나? | int |
recordSeparatorPolicy - 각 줄의 마지막을 정의(정의 안하면 개행문자로 넘어감) | RecordSeparatorPolicy |
resource | Resource |
skippedLinesCallback - 라인을 스킵할때, 콜백을 호출해서 원하는 작업 수행 | LineCallbackHandler |
strict - 파일이 없는 경우에 throw를 발생시킨다.(false로 설정할 경우, 배치처리는 정상적으로 진행된다.) | boolean |
실습
Configuration 소스코드
@Configuration
public class ItemReaderJobConfiguration {
@Bean
public Job job(
JobRepository jobRepository,
Step step){
return new JobBuilder("itemReaderJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(step)
.build();
}
@Bean
public Step step(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
ItemReader<User> flatFileItemReader
){
return new StepBuilder("step", jobRepository)
.<User, User>chunk(2, transactionManager)
.reader(flatFileItemReader)
.writer(System.out::println)
.build();
}
@Bean
public FlatFileItemReader<User> flatFileItemReader(){
return new FlatFileItemReaderBuilder<User>()
.name("flatFileItemReader")
.resource(new ClassPathResource("users.txt"))
.linesToSkip(2)
.delimited().delimiter(",")
.names("name", "age", "region", "telephone")
.targetType(User.class)
.build();
}
}
그렇다면 구분자가 없을때는 어떻게 데이터를 구별해서 읽을 수 있을까?
@Bean
public FlatFileItemReader<User> fixedLengthFlatFileItemReader(){
return new FlatFileItemReaderBuilder<User>()
.name("fixedLengthFlatFileItemReader")
.resource(new ClassPathResource("usersFixedLength.txt"))
.linesToSkip(2)
.fixedLength()
.columns(new Range[]{new Range(1,2), new Range(3,4), new Range(5,6), new Range(7,19)})
.names("name", "age", "region", "telephone")
.targetType(User.class)
.strict(true)
.build();
}
Range로 직접 가져오는 방법이 있다.
JSON
JSON의 경우 Flat 보다 훨씬 간단하다. 개발을 해본 사람이라면 다들 알고 있을 JSON 파일이다.
[
{
"name":"승우",
"age":26,
"region":"서울",
"telephone":010-1234-5678"
},
{
"name":"길동",
"age":9999,
"region":"부산",
"telephone":"010-8765-4321"
}
]
데이터가 많아질수록 보기 편하다는 장점이 있다.
당연히 Spring에서 간단하게 코드 몇줄 작성하면 읽어들일수 있는 기능이 있다.
내가 원하는 클래스로 사용할 수 있다. 이것을 전략패턴 이라고 한다.
스프링은 정말 미친 프레임워크다,,,,, 다되어 있다 진짜로,,,
실습
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
그냥 코드를 짜고 돌리면 의존성 오류가 난다. 꼭 build.gradle에 json의존성을 추가하고 진행하자!
@Bean
public JsonItemReader<User> jsonItemReader(){
return new JsonItemReaderBuilder<User>()
.name("jsonItemReader")
.resource(new ClassPathResource("users.json"))
.jsonObjectReader(new JacksonJsonObjectReader<>(User.class))
.build();
}
출력이 잘 되는 것을 확인할 수 있다.
- Database
읽는 방법이 Paging과, Cursor 두가지로 나뉜다.
Paging : 여러건 조회
Cursor : 단건 조회(BUT, connetion 유지를 해줘야하므로, 연결시간을 길게 잡아줘야 한다.)
또한 여러가지 구현체들이 있지만, 자주 사용하는 Jpa를 살펴보겠다.
JpaPagingItemReaderBuilder의 여러가지 속성
queryProvider(JpaQueryProvider) | 쿼리에 대한 제공자 |
saveState(boolean) | executeContext에 상태를 저장할지 여부 저장하면 다시 돌릴때 그 지점부터 시작 |
entityManagerFactory(EntityManagerFactory) | |
queryString(String) | |
currentItemCount(int) | |
parameterValues(Map<String, Object>) | SQL쓸 때 필요한 파라미터값을 Map으로 제공가능 |
transacted(boolean) | |
maxItemCount(int) | |
name(String) | |
pageSize(int) | |
build() |
JpaCursorItemReaderBuilder의 속성은 Paging과 다를게 없으므로 넘어가겠다.
- HTTP API
- Message Queue
실습
실습에 앞서 datagrip으로 DB에 테이블을 직접 추가 해줬다.
application.properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
또한 이름으로 테이블을 실제 DB 테이블과 매핑 해주기위해서 테이블 이름으로 매핑해주는 설정을 추가해주자.
@Data
@Entity
@Table(name = "USER")
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String name;
private String age;
private String region;
private String telephone;
}
JPA에서 Entity 프록시를 만들기 위해 반드시 기본 생성자가 하나를 생성해야 하기 때문에 @NoArgsConstructor 어노테이션을 붙여준다.
이제 DB에 대한 연결은 끝났다. 테스트를 해보자
paging
@Bean
public ItemReader<User> jpaPagingItemReader(
EntityManagerFactory entityManagerFactory
){
return new JpaPagingItemReaderBuilder<User>()
.name("jpaPagingItemReader")
.entityManagerFactory(entityManagerFactory)
.pageSize(3)
.queryString("SELECT u FROM User u ORDER BY u.id")
.build();
}
데이터를 잘 가지고 오는 것을 확인 할수있다.
cursor
@Bean
public ItemReader<User> jpaCursorItemReader(
EntityManagerFactory entityManagerFactory
){
return new JpaCursorItemReaderBuilder<User>()
.name("jpaCursorItemReader")
.entityManagerFactory(entityManagerFactory)
.queryString("SELECT u FROM User u ORDER BY u.id")
.build();
}
더 쉽다,,,
이렇게 간단하게 ItemReader에 대해서 알아보았다. 다음은 ItemWriter 차례이다.
2024.03.20 - [Spring/Batch Programming] - Spring Batch 3 - Items(Writer)
'Spring > Batch Programming' 카테고리의 다른 글
Spring Batch 3 - Items(Processor) (0) | 2024.03.25 |
---|---|
Spring Batch 3 - Items(Writer) (0) | 2024.03.20 |
Spring Batch Step (0) | 2024.03.13 |
Spring Batch Job (0) | 2024.03.13 |
Spring Batch 도메인 용어, 실전 (3) | 2024.03.12 |