23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
JdbcPagingItemReader는 Spring Batch에서 제공하는 ItemReader로, 데이터베이스로부터 데이터를 페이지 단위로 읽는다.
대규모 데이터 처리 효율성: 메모리 사용량을 최소화하고 커밋 간격을 설정하여 대규모 데이터를 효율적으로 처리할 수 있다.
쿼리 최적화: SQL 쿼리를 직접 작성하여 최적화된 데이터 읽기가 가능하다.
커서 제어: 데이터베이스 커서를 사용하여 데이터 순회를 제어할 수 있다.
DataSource: 데이터베이스 연결 정보를 설정한다.
SqlQuery: 데이터를 읽을 SQL 쿼리를 설정한다.
RowMapper: SQL 쿼리 결과를 Item으로 변환하는 역할을 한다.
PageSize: 페이지 크기를 설정한다.
SkippableItemReader: 오류 발생 시 해당 Item을 건너뛸 수 있도록 한다.
ReadListener: 읽기 시작, 종료, 오류 발생 등의 이벤트를 처리할 수 있도록 한다.
SaveStateCallback: 잡 중단 시 현재 상태를 저장하여 재시작 시 이어서 처리할 수 있도록 한다.
@Data
public class Customer {
private String name;
private int age;
private String gender;
}쿼리 Provider는 실제 배치를 위해서 데이터를 읽어올 쿼리를 작성한다.
@Bean
public PagingQueryProvider queryProvider() throws Exception {
SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean();
queryProvider.setDataSource(dataSource); // DB 에 맞는 PagingQueryProvider 를 선택하기 위함
queryProvider.setSelectClause("id, name, age, gender");
queryProvider.setFromClause("from customer");
queryProvider.setWhereClause("where age >= :age");
Map<String, Order> sortKeys = new HashMap<>(1);
sortKeys.put("id", Order.DESCENDING);
queryProvider.setSortKeys(sortKeys);
return queryProvider.getObject();
}SqlPagingQueryProviderFactoryBean: 쿼리 프로파이더 팩토리
setDataSource: 데이터소스를 설정한다.
setSelectClause: select에서 프로젝션할 필드 이름을 지정한다.
setFromClause: 조회할 테이블
setWhereClause: 조건절
setSortKeys: 소트 키를 지정한다.
@Bean
public JdbcPagingItemReader<Customer> jdbcPagingItemReader() throws Exception {
Map<String, Object> parameterValue = new HashMap<>();
parameterValue.put("age", 20);
return new JdbcPagingItemReaderBuilder<Customer>()
.name("jdbcPagingItemReader")
.fetchSize(CHUNK_SIZE)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(Customer.class))
.queryProvider(queryProvider())
.parameterValues(parameterValue)
.build();
}package com.schooldevops.springbatch.batchsample.jobs.jdbc;
import com.schooldevops.springbatch.batchsample.jobs.models.Customer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JdbcPagingItemReader;
import org.springframework.batch.item.database.Order;
import org.springframework.batch.item.database.PagingQueryProvider;
import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder;
import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Configuration
public class JdbcPagingReaderJobConfig {
/**
* CHUNK 크기를 지정한다.
*/
public static final int CHUNK_SIZE = 2;
public static final String ENCODING = "UTF-8";
public static final String JDBC_PAGING_CHUNK_JOB = "JDBC_PAGING_CHUNK_JOB";
@Autowired
DataSource dataSource;
@Bean
public JdbcPagingItemReader<Customer> jdbcPagingItemReader() throws Exception {
Map<String, Object> parameterValue = new HashMap<>();
parameterValue.put("age", 20);
return new JdbcPagingItemReaderBuilder<Customer>()
.name("jdbcPagingItemReader")
.fetchSize(CHUNK_SIZE)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(Customer.class))
.queryProvider(queryProvider())
.parameterValues(parameterValue)
.build();
}
@Bean
public PagingQueryProvider queryProvider() throws Exception {
SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean();
queryProvider.setDataSource(dataSource); // DB 에 맞는 PagingQueryProvider 를 선택하기 위함
queryProvider.setSelectClause("id, name, age, gender");
queryProvider.setFromClause("from customer");
queryProvider.setWhereClause("where age >= :age");
Map<String, Order> sortKeys = new HashMap<>(1);
sortKeys.put("id", Order.DESCENDING);
queryProvider.setSortKeys(sortKeys);
return queryProvider.getObject();
}
@Bean
public FlatFileItemWriter<Customer> customerFlatFileItemWriter() {
return new FlatFileItemWriterBuilder<Customer>()
.name("customerFlatFileItemWriter")
.resource(new FileSystemResource("./output/customer_new_v1.csv"))
.encoding(ENCODING)
.delimited().delimiter("\t")
.names("Name", "Age", "Gender")
.build();
}
@Bean
public Step customerJdbcPagingStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
log.info("------------------ Init customerJdbcPagingStep -----------------");
return new StepBuilder("customerJdbcPagingStep", jobRepository)
.<Customer, Customer>chunk(CHUNK_SIZE, transactionManager)
.reader(jdbcPagingItemReader())
.writer(customerFlatFileItemWriter())
.build();
}
@Bean
public Job customerJdbcPagingJob(Step customerJdbcPagingStep, JobRepository jobRepository) {
log.info("------------------ Init customerJdbcPagingJob -----------------");
return new JobBuilder(JDBC_PAGING_CHUNK_JOB, jobRepository)
.incrementer(new RunIdIncrementer())
.start(customerJdbcPagingStep)
.build();
}
}output 디렉토리에 customer_new_v1.csv 파일이 생성되고 내용은 다음과 같다.
KIDO9 41 M
KIDO8 33 F
KIDO7 25 M
KIDO4 40 M
KIDO3 30 M
KIDO2 20 M지금까지 JdbcPagingItemReader를 이용하여 데이터베이스의 내용을 읽어서, 파일로 저장해 보았다.
JdbcPagingItemReader은 데이터소스를 주입받고, QueryProvider를 이용하여 쿼리를 작성하는 코드도 작성해보았다.
페이징 처리의 성능을 개선하면, 매우 큰 데이터도 효율적으로 수행할 수 있다.
페이징은 pageSize로 지정된 수만큼 읽어오고, 이를 청크로 전달된다는 것도 확인할 수 있었다.
JdbcBatchItemWriter Spring Batch에서 제공하는 ItemWriter 인터페이스를 구현하는 클래스이다.
데이터를 JDBC를 통해 데이터베이스에 저장하는 데 사용된다.
DataSource: 데이터베이스 연결 정보를 지정한다.
SqlStatementCreator: INSERT 쿼리를 생성하는 역할을 한다.
PreparedStatementSetter: INSERT 쿼리의 파라미터 값을 설정하는 역할을 한다.
ItemSqlParameterSourceProvider: Item 객체를 기반으로 PreparedStatementSetter에 전달할 파라미터 값을 생성하는 역할을 한다.
데이터베이스 연동: JDBC를 통해 다양한 데이터베이스에 데이터를 저장할 수 있다.
성능: 대량의 데이터를 빠르게 저장할 수 있다.
유연성: 다양한 설정을 통해 원하는 방식으로 데이터를 저장할 수 있다.
설정 복잡성: JDBC 설정 및 쿼리 작성이 복잡할 수 있다.
오류 가능성: 설정 오류 시 데이터 손상 가능성이 있다.
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8&clusterInstanceHostPattern=?&zeroDateTimeBehavior=CONVERT_TO_NULL&allowMultiQueries=true
username: root
password: root1234
batch:
job:
name: JDBC_BATCH_WRITER_CHUNK_JOBcreate table testdb.customer2
(
id int auto_increment primary key,
name varchar(100) null,
age int null,
gender varchar(10) null
); @Bean
public JdbcBatchItemWriter<Customer> flatFileItemWriter() {
return new JdbcBatchItemWriterBuilder<Customer>()
.dataSource(dataSource)
.sql("INSERT INTO customer2 (name, age, gender) VALUES (?, ?, ?)")
.itemSqlParameterSourceProvider(new CustomerItemSqlParameterSourceProvider())
.build();
}package com.schooldevops.springbatch.batchsample.jobs.flatfilereader;
import com.schooldevops.springbatch.batchsample.jobs.models.Customer;
import org.springframework.batch.item.database.ItemSqlParameterSourceProvider;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
public class CustomerItemSqlParameterSourceProvider implements ItemSqlParameterSourceProvider<Customer> {
@Override
public SqlParameterSource createSqlParameterSource(Customer item) {
return new BeanPropertySqlParameterSource(item);
}
}package com.schooldevops.springbatch.batchsample.jobs.flatfilereader;
import com.schooldevops.springbatch.batchsample.jobs.models.Customer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Slf4j
@Configuration
public class JdbcBatchItemJobConfig {
/**
* CHUNK 크기를 지정한다.
*/
public static final int CHUNK_SIZE = 100;
public static final String ENCODING = "UTF-8";
public static final String JDBC_BATCH_WRITER_CHUNK_JOB = "JDBC_BATCH_WRITER_CHUNK_JOB";
@Autowired
DataSource dataSource;
@Bean
public FlatFileItemReader<Customer> flatFileItemReader() {
return new FlatFileItemReaderBuilder<Customer>()
.name("FlatFileItemReader")
.resource(new ClassPathResource("./customer.csv"))
.encoding(ENCODING)
.delimited().delimiter(",")
.names("name", "age", "gender")
.targetType(Customer.class)
.build();
}
@Bean
public JdbcBatchItemWriter<Customer> flatFileItemWriter() {
return new JdbcBatchItemWriterBuilder<Customer>()
.dataSource(dataSource)
.sql("INSERT INTO customer2 (name, age, gender) VALUES (:name, :age, :gender)")
.itemSqlParameterSourceProvider(new CustomerItemSqlParameterSourceProvider())
.build();
}
@Bean
public Step flatFileStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init flatFileStep -----------------");
return new StepBuilder("flatFileStep", jobRepository)
.<Customer, Customer>chunk(CHUNK_SIZE, transactionManager)
.reader(flatFileItemReader())
.writer(flatFileItemWriter())
.build();
}
@Bean
public Job flatFileJob(Step flatFileStep, JobRepository jobRepository) {
log.info("------------------ Init flatFileJob -----------------");
return new JobBuilder(JDBC_BATCH_WRITER_CHUNK_JOB, jobRepository)
.incrementer(new RunIdIncrementer())
.start(flatFileStep)
.build();
}
}작업을 완료하면 customer2 테이블에 다음과 같이 데이터가 인서트 된 것을 확인할 수 있다.
id,name,age,gender
1,unclebae,40,Male
2,superman,45,Male
3,WonderWoman,30,FemaleJdbcBatchItemWriter 를 작성하였다.
jdbc를 이용하고, sqlquery를 이용하여 직접 쿼리를 작성하고, 파라미터로 Customer 객체의 필드 이름을 세팅한결과 정상적으로 데이터가 저장되었다.
간단한 처리, 혹은 쿼리를 직접 작성해야하는 경우 유용한 JdbcBatchItemWriter에 대해 알수 있었다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.