[5회차] JdbcPagingItemReader로 DB내용을 읽고, JdbcBatchItemWriter로 DB에 쓰기

아래 글은 한국 스프링 사용자 모임(KSUG)에서 진행된 스프링 배치 스터디 내용을 정리한 게시글입니다.
DEVOCEAN에 연재 중인 KIDO님의 글을 참고하여 실습한 내용을 기록했습니다.

 

 

[SpringBatch 연재 05] JdbcPagingItemReader로 DB내용을 읽고, JdbcBatchItemWriter로 DB에 쓰기

 

devocean.sk.com

원본: [SpringBatch 연재 05] JdbcPagingItemReader로 DB내용을 읽고, JdbcBatchItemWriter로 DB에 쓰기

 

지난 시간에는 FlatFileItemReader FlatFileItemWrite를 이용하여 Batch Job을 실행해 보았습니다.

 

이번 시간에는 Jdbc를 활용해서 데이터베이스를 이용한 Batch Job을 실행해 보겠습니다.

DB는 각자 생성해 두었다는 것을 전제로 실습을 진행하겠습니다. 

 


1. JdbcPagingItemReader

1.1. JdbcPagingItemReader 개요

  • JdbcPagingItemReader는 Spring Batch에서 제공하는 ItemReader로, 데이터베이스로부터 데이터를 페이지 단위로 읽는다.
기능 설명
대규모 데이터 처리 효율성 메모리 사용량을 최소화하고 커밋 간격을 설정하여 대규모 데이터를 효율적으로 처리할 수 있다.
쿼리 최적화 SQL 쿼리를 직접 작성하여 최적화된 데이터 읽기가 가능하다.
커서 제어 데이터베이스 커서를 사용하여 데이터 순회를 제어할 수 있다.

 

1.2. JdbcPagingItemReader 주요 구성 요소

항목 설명
DataSource 데이터베이스 연결 정보를 설정한다.
SqlQuery 데이터를 읽을 SQL 쿼리를 설정한다.
RowMapper SQL 쿼리 결과를 Item으로 변환하는 역할을 한다.
PageSize 페이지 크기를 설정한다.
SkippableItemReader 오류 발생 시 해당 Item을 건너뛸 수 있도록 한다.
ReadListener 읽기 시작, 종료, 오류 발생 등의 이벤트를 처리할 수 있도록 한다.
SaveStateCallback 잡 중단 시 현재 상태를 저장하여 재시작 시 이어서 처리할 수 있도록 한다.

 

1.3. PagingItemReader 실습하기

1.3.1. Customer 클래스 생성

@Data
public class Customer {

    private String name;
    private int age;
    private String gender;
    
}

 


🤔 잠깐 알아보기: Lombok의 @Data 애노테이션이란?

Java 클래스에 여러 유용한 메서드와 기능을 자동으로 추가해주는 애노테이션입니다. 이 애노테이션을 클래스에 붙이면 @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode 애노테이션의 기능이 모두 포함됩니다.

 

@Data가 포함하는 애노테이션 기능

  • @Getter: 모든 필드에 대한 getter 메서드를 생성합니다.
  • @Setter: 모든 필드에 대한 setter 메서드를 생성합니다.
  • @RequiredArgsConstructor: final 또는 @NonNull이 붙은 필드만을 포함하는 생성자를 생성합니다.
  • @ToString: toString() 메서드를 자동으로 생성합니다.
  • @EqualsAndHashCode: 객체 간의 동등성 비교를 위한 equals()와 hashCode() 메서드를 생성합니다.

 

주의 사항

  • @Data 애노테이션은 모든 필드에 대해 getter와 setter를 생성하기 때문에, 불필요한 메서드가 추가될 수 있습니다. 불변 객체를 만들고자 할 때는 @Data 애노테이션보다는 @Value 애노테이션을 사용하는 것이 좋습니다.

 

 


1.3.2. 쿼리 Provider 메서드 생성하기

실제 배치를 위해 데이터를 읽어올 쿼리를 작성합니다.

@Bean
public PagingQueryProvider queryProvider() throws Exception {
    /**
     * SqlPagingQueryProviderFactoryBean
     * Spring Batch에서 페이징 쿼리를 쉽게 생성하기 위한 팩토리 빈입니다.
     * 이 클래스를 사용하면 데이터베이스 종류에 따라 최적화된 페이징 쿼리를 자동으로 생성할 수 있습니다.
     */
    SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean();
    queryProvider.setDataSource(dataSource);  // DB 설정하기
    queryProvider.setSelectClause("id, name, age, gender"); // 사용할 쿼리의 SELECT 절을 지정하는 메서드(읽어올 컬럼들 정의)
    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();
}

1.3.3. jdbcPagingItemReader 메서드 생성하기

해당 조건으로 age가 20 이상인 사람이 추출됩니다.

@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();
}

1.4. 전체 실행 파일

@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
         * Spring Batch에서 페이징 쿼리를 쉽게 생성하기 위한 팩토리 빈입니다.
         * 이 클래스를 사용하면 데이터베이스 종류에 따라 최적화된 페이징 쿼리를 자동으로 생성할 수 있습니다.
         */
        SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean();
        queryProvider.setDataSource(dataSource);  // DB 설정하기
        queryProvider.setSelectClause("id, name, age, gender"); // 사용할 쿼리의 SELECT 절을 지정하는 메서드(읽어올 컬럼들 정의)
        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();
    }
}

 

DataSource를 주입해주기 위해 DataSourceConfig 클래스도 생성해주었습니다.

 

@Configuration
public class DataSourceConfig {

    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
                .url(url)
                .username(username)
                .password(password)
                .driverClassName(driverClassName)
                .build();
    }
}

1.4.1. 사용한 테스트 데이터

CREATE TABLE customer (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age INT,
    gender ENUM('M', 'F')
);
INSERT INTO customer (name, age, gender) VALUES 
('Alice', 25, 'F'),
('Bob', 30, 'M'),
('Charlie', 19, 'M'),
('Diana', 22, 'F'),
('Eve', 28, 'F'),
('Frank', 18, 'M'),
('Grace', 32, 'F'),
('Henry', 20, 'M'),
('Ivy', 27, 'F'),
('Jack', 24, 'M');

 

1.5. 실행하기

2024-11-02T16:10:05.376+09:00  INFO 6840 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-11-02T16:10:05.376+09:00  INFO 6840 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.30]
2024-11-02T16:10:05.395+09:00  INFO 6840 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-11-02T16:10:05.396+09:00  INFO 6840 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 420 ms
2024-11-02T16:10:05.422+09:00  INFO 6840 --- [           main] c.e.b.jobs.task01.GreetingTasklet        : ----------------- After Properites Sets() --------------
2024-11-02T16:10:05.428+09:00  INFO 6840 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-11-02T16:10:05.580+09:00  INFO 6840 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@1e3df614
2024-11-02T16:10:05.581+09:00  INFO 6840 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-11-02T16:10:05.641+09:00  INFO 6840 --- [           main] c.e.b.j.t.BasicTaskJobConfiguration      : ------------------ Init myStep -----------------
2024-11-02T16:10:05.652+09:00  INFO 6840 --- [           main] c.e.b.j.t.BasicTaskJobConfiguration      : ------------------ Init myJob -----------------
2024-11-02T16:10:05.660+09:00  INFO 6840 --- [           main] c.e.b.jobs.task04.FlatFileItemJobConfig  : ------------------ Init flatFileStep -----------------
2024-11-02T16:10:05.667+09:00  INFO 6840 --- [           main] c.e.b.jobs.task04.FlatFileItemJobConfig  : ------------------ Init flatFileJob -----------------
2024-11-02T16:10:05.673+09:00  INFO 6840 --- [           main] c.e.b.j.t.JdbcPagingReaderJobConfig      : ------------------ Init customerJdbcPagingStep -----------------
2024-11-02T16:10:05.676+09:00  INFO 6840 --- [           main] c.e.b.j.t.JdbcPagingReaderJobConfig      : ------------------ Init customerJdbcPagingJob -----------------
2024-11-02T16:10:05.845+09:00  INFO 6840 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-11-02T16:10:05.851+09:00  INFO 6840 --- [           main] c.e.batch_sample.BatchSampleApplication  : Started BatchSampleApplication in 1.034 seconds (process running for 1.221)
2024-11-02T16:10:05.852+09:00  INFO 6840 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2024-11-02T16:10:05.915+09:00  INFO 6840 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=JDBC_PAGING_CHUNK_JOB]] launched with the following parameters: [{'run.id':'{value=2, type=class java.lang.Long, identifying=true}'}]
2024-11-02T16:10:05.936+09:00  INFO 6840 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [customerJdbcPagingStep]
2024-11-02T16:10:05.981+09:00  INFO 6840 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [customerJdbcPagingStep] executed in 44ms
2024-11-02T16:10:05.992+09:00  INFO 6840 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=JDBC_PAGING_CHUNK_JOB]] completed with the following parameters: [{'run.id':'{value=2, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 67ms
2024-11-02T16:10:10.929+09:00  INFO 6840 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-11-02T16:10:10.938+09:00  INFO 6840 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Process finished with exit code 130 (interrupted by signal 2:SIGINT)

 

해당 로그와 함께 실행이 완료되며 output/customer_new_v1.csv 파일이 생성됩니다.

Jack	24	M
Ivy	27	F
Henry	20	M
Grace	32	F
Eve	28	F
Diana	22	F
Bob	30	M
Alice	25	F

 

2. JdbcBatchItemWriter

2.1. JdbcBatchItemWriter 개요

  • JdbcBatchItemWriter Spring Batch에서 제공하는 ItemWriter 인터페이스를 구현하는 클래스이다.
  • 데이터를 JDBC를 통해 데이터베이스에 배치로 전송하는 용도로 사용된다.

 

2.2. JdbcBatchItemWriter 구성 요소

항목 설명
DataSource 데이터베이스 연결 정보를 지정한다.
SqlStatementCreator INSERT 쿼리를 생성하는 역할을 한다.
PreparedStatementSetter INSERT 쿼리의 파라미터 값을 설정하는 역할을 한다.
ItemSqlParameterSourceProvider Item 객체를 기반으로 PreparedStatementSetter에 전달할 파라미터 값을 생성하는 역할을 한다.

 

 

구분 항목 설명
장점 데이터베이스 연동 JDBC를 통해 다양한 데이터베이스에 데이터를 저장할 수 있다.
성능 대량의 데이터를 빠르게 저장할 수 있다.
유연성 다양한 설정을 통해 원하는 방식으로 데이터를 저장할 수 있다.
단점 설정 복잡성 JDBC 설정 및 쿼리 작성이 복잡할 수 있다.
데이터베이스 종속 특정 데이터베이스에 종속적이다.
오류 가능성 설정 오류 시 데이터 손상 가능성이 있다.

 

2.3. JdbcBatchItemWriter 실습 하기

2.4. 전체 코드

 

이번 실습에서는 customer.csv 파일에서 데이터를 읽어와 데이터베이스에 추가해 보겠습니다.

이전 실습에서 사용했던 customer.csv 파일을 그대로 활용합니다.

 

또한 빈을 생성할 때 동일한 이름의 빈이 중복되지 않도록 JdbcBatchItemJobConfig 클래스에서 빈 이름에 task05XXX라는 이름을 붙여 주었습니다.

 

2.4.1. 사용한 customer.svc 파일

Alice,30,F
Bob,45,M
Charlie,25,M
Diana,29,F
Evan,35,M
Fiona,40,F
George,55,M
Hannah,32,F

 

2.4.2. JobConfig 클래스

@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> task05FlatFileItemReader() {
        // FlatFileItemReader를 생성하여 CSV 파일에서 데이터를 읽어오는 설정을 합니다.
        return new FlatFileItemReaderBuilder<Customer>()
                .name("Task05FlatFileItemReader") // Reader 의 이름을 설정
                .resource(new ClassPathResource("./customer.csv")) // 읽어올 CSV 파일 경로를 지정
                .encoding(ENCODING) // 파일 인코딩 설정
                .delimited().delimiter(",") // 파일의 구분자를 ','로 설정
                .names("name", "age", "gender") // CSV 파일의 컬럼 이름을 설정하여 매핑
                .targetType(Customer.class) // 매핑할 대상 객체 타입을 Customer로 설정
                .build();
    }

    @Bean
    public JdbcBatchItemWriter<Customer> task05FlatFileItemWriter() {
        // JdbcBatchItemWriter를 생성하여 데이터를 데이터베이스에 삽입하는 설정을 합니다.
        return new JdbcBatchItemWriterBuilder<Customer>()
                .dataSource(dataSource) // 사용할 데이터베이스의 DataSource 설정
                .sql("INSERT INTO customer (name, age, gender) VALUES (:name, :age, :gender)") // 삽입할 SQL 쿼리 정의
                .itemSqlParameterSourceProvider(new CustomItemSqlParameterSourceProvider()) // SQL 쿼리에 매핑할 파라미터 제공자 설정
                .build();
    }

    @Bean
    public Step tesk05FlatFileStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        log.info("------------------ Init flatFileStep -----------------");

        return new StepBuilder("flatFileStep", jobRepository)
                .<Customer, Customer>chunk(CHUNK_SIZE, transactionManager)
                .reader(task05FlatFileItemReader())
                .writer(task05FlatFileItemWriter())
                .build();
    }

    @Bean
    public Job task05FlatFileJob(Step tesk05FlatFileStep, JobRepository jobRepository) {
        log.info("------------------ Init flatFileJob -----------------");
        return new JobBuilder(JDBC_BATCH_WRITER_CHUNK_JOB, jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(tesk05FlatFileStep)
                .build();
    }
}

 

2.3.3. ParameterSourceProvider 클래스 생성

ParameterSourceProvider 클래스는 SQL 쿼리에 매핑할 파라미터 제공자로, Spring Batch에서 JdbcBatchItemWriter를 사용할 때 SQL 쿼리에 데이터 매핑을 자동으로 처리해 주는 역할을 합니다. 각 Item의 필드를 SQL 쿼리의 파라미터에 맞게 변환해 주는 것입니다.

 

역할 및 작동 방식

  • 매핑 역할: 쿼리에서 :name, :age, :gender와 같은 이름의 파라미터에, Customer 객체의 필드 값을 자동으로 매핑합니다. 이를 통해 SQL 쿼리와 객체 필드를 연결하여, 값이 올바르게 삽입될 수 있도록 돕습니다.
  • 작동 방식: JdbcBatchItemWriter는 각 Item(여기서는 Customer 객체)의 필드 이름과 SQL 파라미터 이름을 비교하여 자동으로 매핑합니다. 예를 들어, Customer 객체의 name 필드는 SQL 쿼리의 :name 파라미터에 매핑됩니다.
public class CustomItemSqlParameterSourceProvider implements ItemSqlParameterSourceProvider<Customer> {
    @Override
    public SqlParameterSource createSqlParameterSource(Customer item) {
        return new BeanPropertySqlParameterSource(item);
    }
}

 

2.4. 실행하기

실습을 위해 사용했던 customer 테이블 내에 데이터들을 지워주었습니다.

mysql> DELETE FROM customer;
Query OK, 10 rows affected (0.00 sec)

mysql> select * from customer;
Empty set (0.00 sec)
2024-11-02T17:22:18.258+09:00  INFO 8618 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.30]
2024-11-02T17:22:18.277+09:00  INFO 8618 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-11-02T17:22:18.278+09:00  INFO 8618 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 439 ms
2024-11-02T17:22:18.307+09:00  INFO 8618 --- [           main] c.e.b.jobs.task01.GreetingTasklet        : ----------------- After Properites Sets() --------------
2024-11-02T17:22:18.318+09:00  INFO 8618 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-11-02T17:22:18.466+09:00  INFO 8618 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@5d318e91
2024-11-02T17:22:18.466+09:00  INFO 8618 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-11-02T17:22:18.515+09:00  INFO 8618 --- [           main] c.e.b.j.t.BasicTaskJobConfiguration      : ------------------ Init myStep -----------------
2024-11-02T17:22:18.527+09:00  INFO 8618 --- [           main] c.e.b.j.t.BasicTaskJobConfiguration      : ------------------ Init myJob -----------------
2024-11-02T17:22:18.535+09:00  INFO 8618 --- [           main] c.e.b.jobs.task04.FlatFileItemJobConfig  : ------------------ Init flatFileStep -----------------
2024-11-02T17:22:18.542+09:00  INFO 8618 --- [           main] c.e.b.jobs.task04.FlatFileItemJobConfig  : ------------------ Init flatFileJob -----------------
2024-11-02T17:22:18.548+09:00  INFO 8618 --- [           main] c.e.b.j.t.r.JdbcPagingReaderJobConfig    : ------------------ Init customerJdbcPagingStep -----------------
2024-11-02T17:22:18.551+09:00  INFO 8618 --- [           main] c.e.b.j.t.r.JdbcPagingReaderJobConfig    : ------------------ Init customerJdbcPagingJob -----------------
2024-11-02T17:22:18.552+09:00  INFO 8618 --- [           main] c.e.b.j.t.writer.JdbcBatchItemJobConfig  : ------------------ Init flatFileStep -----------------
2024-11-02T17:22:18.554+09:00  INFO 8618 --- [           main] c.e.b.j.t.writer.JdbcBatchItemJobConfig  : ------------------ Init flatFileJob -----------------
2024-11-02T17:22:18.714+09:00  INFO 8618 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-11-02T17:22:18.719+09:00  INFO 8618 --- [           main] c.e.batch_sample.BatchSampleApplication  : Started BatchSampleApplication in 1.051 seconds (process running for 1.254)
2024-11-02T17:22:18.721+09:00  INFO 8618 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2024-11-02T17:22:18.788+09:00  INFO 8618 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=JDBC_BATCH_WRITER_CHUNK_JOB]] launched with the following parameters: [{'run.id':'{value=4, type=class java.lang.Long, identifying=true}'}]
2024-11-02T17:22:18.814+09:00  INFO 8618 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [flatFileStep]
2024-11-02T17:22:18.847+09:00  INFO 8618 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [flatFileStep] executed in 33ms
2024-11-02T17:22:18.857+09:00  INFO 8618 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=JDBC_BATCH_WRITER_CHUNK_JOB]] completed with the following parameters: [{'run.id':'{value=4, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 60ms

mysql> select * from customer;
+----+---------+------+--------+
| id | name    | age  | gender |
+----+---------+------+--------+
| 11 | Alice   |   30 | F      |
| 12 | Bob     |   45 | M      |
| 13 | Charlie |   25 | M      |
| 14 | Diana   |   29 | F      |
| 15 | Evan    |   35 | M      |
| 16 | Fiona   |   40 | F      |
| 17 | George  |   55 | M      |
| 18 | Hannah  |   32 | F      |
+----+---------+------+--------+
8 rows in set (0.00 sec)

 

새로운 데이터가 생성된 것을 볼 수 있습니다.

 

3. Wrap Up

데이터베이스를 활용하여 JdbcPagingItemReader와 JdbcBatchItemWriter의 간단한 실습을 진행해보았습니다.