기존에 실습해봤던 템플릿 기반으로 만든 ItemReaderInterFace 와 거의 흡사합니다.
JdbcBatchItemWriter을 사용하는 이유는
1. bulk insert/update/delete처리
예시) insert into person (name, age, address) values (1,2,3), (4,5,6), (7,8,9);
* 단건 처리가 아니기 때문에 비교적 높은 성능을 낼 수 있습니다.
csv & JDBC & JPA 소스 화면
@Configuration
@Slf4j
public class ItemWriterConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final DataSource dataSource;
private final EntityManagerFactory entityManagerFactory;
public ItemWriterConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, DataSource dataSource, EntityManagerFactory entityManagerFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
this.dataSource = dataSource;
this.entityManagerFactory = entityManagerFactory;
}
@Bean
public Job itemWriterJob() throws Exception {
return this.jobBuilderFactory.get("itemWriterJob")
.incrementer(new RunIdIncrementer())
.start(this.csvItemWriterStep())
.next(this.jdbcBatchItemWriterStep())
.next(this.jpaItemWriterStep())
.build();
}
@Bean
public Step jdbcBatchItemWriterStep() {
return stepBuilderFactory.get("jdbcBatchItemWriterStep")
.<Person, Person>chunk(10)
.reader(itemReader())
.writer(jdbcBatchItemWriter())
.build();
}
@Bean
public Step jpaItemWriterStep() throws Exception {
return stepBuilderFactory.get("jpaItemWriterStep")
.<Person, Person>chunk(10)
.reader(itemReader())
.writer(jpaItemWriter())
.build();
}
private ItemWriter<Person> jpaItemWriter() throws Exception {
JpaItemWriter<Person> itemWriter = new JpaItemWriterBuilder<Person>()
.entityManagerFactory(entityManagerFactory)
.usePersist(true) // Spring Batch 4.2에서 도입된 persist 모드를 활성화
.build();
itemWriter.afterPropertiesSet();
return itemWriter;
}
private ItemWriter<Person> jdbcBatchItemWriter() {
JdbcBatchItemWriter<Person> itemWriter = new JdbcBatchItemWriterBuilder<Person>()
.dataSource(dataSource)
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql("insert into person(name, age, address) values(:name, :age, :address)")
.build();
itemWriter.afterPropertiesSet();
return itemWriter;
};
@Bean
public Step csvItemWriterStep() throws Exception {
return this.stepBuilderFactory.get("csvItemWriterStep")
.<Person, Person>chunk(10)
.reader(itemReader())
.writer(csvFileItemWriter())
.build();
}
@Bean
public ItemWriter<Person> csvFileItemWriter() throws Exception {
BeanWrapperFieldExtractor<Person> fieldExtractor = new BeanWrapperFieldExtractor<>();
fieldExtractor.setNames(new String[]{"id", "name", "age", "address"});
//매핑 설정
DelimitedLineAggregator<Person> lineAggregator = new DelimitedLineAggregator<>();
lineAggregator.setDelimiter(",");
lineAggregator.setFieldExtractor(fieldExtractor);
FlatFileItemWriter<Person> itemWriter = new FlatFileItemWriterBuilder<Person>()
.name("csvFileItemWriter")
.encoding("UTF-8")
.resource(new FileSystemResource("output/test-output.csv"))
.lineAggregator(lineAggregator)
.headerCallback(writer -> writer.write("id,이름,나이,주소"))
.footerCallback(writer -> writer.write("---------------\n")) // 개행문자를 통해
.append(true) // 문자열 추가 가능.
.build();
itemWriter.afterPropertiesSet();
return itemWriter;
}
private ItemReader<Person> itemReader() {
return new CustomItemReader<>(getItems());
}
private List<Person> getItems() {
List<Person> items = new ArrayList<>();
for (int i = 0; i < 100; i++) {
items.add(new Person("test name" +i, "test age", "test address"));
}
return items;
}
}
resource를 통해 CSV로 작업하면 output 디렉토리가 생성되면서 해당 파일이 만들어진다.
jdbc를 통해 작업하면 db에서 select시 적재된것을 확인할 수 있다.
JPA를 작업하기 위해서는 Person 클래스를 엔티티로 변경한다.
@Entity
@NoArgsConstructor
@Getter
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String age;
private String address;
public Person(String name, String age, String address) {
this(0, name, age, address);
}
public Person(int id, String name, String age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
}
jpa 설정을 추가함으로써 update 설정을 통해 merge를 체크할 수 있다.
# 2.4.2 버전에서 2.5.2로 개편 후 hikari 삭제
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_batch?characterEncoding=UTF-8&serverTimezone=UTC&rewriteBatchedStatments=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
# format_sql: true
batch:
initialize-schema: never
jpa 적용시 select를 확인하고 insert를 진행한다.
JPA를 적용 후 실행하기전에 .next(this.jdbcBatchItemWriterStep())를 주석하고 실습하길 권장한다.
바로 돌리게 될 경우 아래의 오류를 발견할 수 있습니다.
기존에 엔티티를 통해 자동생성 하지않고 Person 직접 생성하여(새로운 엔티티) merge가 존재한것으로 체크
Entity를 하나씩 EntityManager.persist 또는 EntityManager.merge로 insert 방식으로 나뉘며,
usePersist(true)를 통해 Merge와 달리 Insert쿼리만 발생한 것을 확인할 수 있습니다.
merge와 persist정도의 간단한 실습을 마쳤으나, 추후에는 아래의 블로그로 참고한것을 실습 해 볼 예정입니다.
참조사이트 : Spring Batch ItemWriter 성능 비교
'SPRING > 기본 문법' 카테고리의 다른 글
[SPRING] Template 파일 만들기 및 ItemReaderInterFace 구조 & CSV, JDBC 데이터 읽기 실습 (0) | 2021.07.24 |
---|---|
[SPRING] Tasklet 방식과 Chunk 방식 구현 (0) | 2021.07.21 |
[SPRING] Spring Batch 환경 설정 (0) | 2021.07.20 |
[JPA] Entity의 가독성 높이기 (@Enbedded, @Embeddable) (0) | 2021.06.09 |
[SPRING] 빈 생명주기 콜백 (0) | 2021.04.27 |