[SpringBatch] DB 데이터 읽고 쓰기

한지연·2023년 9월 19일
0

❗️ 자바17, 스프링부트3.1.3, 스프링배치5.0.3, MySql을 이용하여 코드를 작성하였습니다.
❗️ 가입한 회원들을 모아 한 번에 쿠폰을 발급해주는 배치 프로그램입니다.

1. 의존성 주입

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-batch'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.batch:spring-batch-test'
}

2. yml 파일 설정

spring:
  batch:
    job:
      name: ${job.name:dbDataJob}
      enabled: true
    jdbc:
      initialize-schema: always
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/batch_study?serverTimezone=Asia/Seoul
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: cos
    password: cos1234
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update # 옵션 종류는 본인이 하고 싶은 것으로 사용하면 된다.

3. 객체 생성

  • User
@Entity
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    private String name;
    private int age;
    private String phone;

    private String email;

    @OneToMany(mappedBy = "user")
    private List<SendCoupon> userCouponList = new ArrayList<>();
}
  • SendCoupon
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
@Getter
public class SendCoupon {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long c_id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id")
    private User user;

    @CreationTimestamp
    private Date issudate;

    @Column(columnDefinition = "boolean default false")
    private boolean isUsed;

    public SendCoupon(User user) {
        this.user = user;
    }
}

4. 더미데이터 생성

-- user dummy data 생성
insert into batch_study.users(`name`, `age`, `phone`, `email`) values ('최형우', 39, '010-****-****', 'best1@test.com');
insert into batch_study.users(`name`, `age`, `phone`, `email`) values ('김도영', 19, '010-****-****', 'best2@test.com');
insert into batch_study.users(`name`, `age`, `phone`, `email`) values ('나성범', 33, '010-****-****', 'best3@test.com');
insert into batch_study.users(`name`, `age`, `phone`, `email`) values ('양현종', 35, '010-****-****', 'best4@test.com');
insert into batch_study.users(`name`, `age`, `phone`, `email`) values ('윤영철', 19, '010-****-****', 'best5@test.com')

select * from batch_study.users;
select * from batch_study.send_coupon;

5. Job, Step 생성

@Configuration
@RequiredArgsConstructor
public class DbDataReadWriteConfig {


    @Autowired
    EntityManagerFactory em;


    @Bean
    public Job trMigrationJob(JobRepository jobRepository, PlatformTransactionManager transactionManager){

        return new JobBuilder("dbDataJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(dbDataStep(jobRepository, transactionManager))
                .build();
    }

    @JobScope
    @Bean
    public Step dbDataStep(
            JobRepository jobRepository,
            PlatformTransactionManager transactionManager

    ) {
        return new StepBuilder("dbDataStep", jobRepository)
                .<User, SendCoupon>chunk(5, transactionManager)
                // chunk는 하나의 트랜잭션으로 보면 됨, 5개 단위로 사용
                .reader(dbDataReader())
                .processor(dbDataProcessor())
                .writer(dbDataWriter())
                .build();
    }


    @StepScope
    @Bean
    public JpaItemWriter<SendCoupon> dbDataWriter(){
        return new JpaItemWriterBuilder<SendCoupon>()
                .entityManagerFactory(em)
                .build();
    }

    @StepScope
    @Bean
    public ItemProcessor<User, SendCoupon> dbDataProcessor(){
        return new ItemProcessor<User, SendCoupon>() {
            @Override
            public SendCoupon process(User user) throws Exception {
                return new SendCoupon(user);
            }
        };
    }
    
	// 1. db에서 데이터를 읽어옴
    @StepScope
    @Bean
    public JpaPagingItemReader<User> dbDataReader(){
        return new JpaPagingItemReaderBuilder<User>()
                .name("dbReader")
                .entityManagerFactory(em)
                .queryString("select o from User o")
                .pageSize(5)
                .build();
    }
  • chunk

    chunk는 간단히 말하자면 한 번의 커밋 때 처리할 데이터 개수다. 롤백 또한 chunk 단위로 작동한다. 내가 작성한 chunk 단위는 5이기 때문에 데이터 5개가 모였을 때 ItemWriter가 실행 된다. 그리고 그림처럼 ItemReader도 chuck 단위가 채워질 때까지 읽는 것을 반복한다.
    🔗 chunk 이해에 도움이 되는 글

🔗 출처 - 스프링배치 공식 문서

profile
배우고 활용하는 것을 즐기는 개발자, 한지연입니다!

0개의 댓글