Spring Batch 기초 실습

jinvicky·2023년 11월 1일
0

Spring & Java

목록 보기
14/23

개요


Spring Batch + Scheduler를 이용해서 기초적인 Job, Step을 등록하고 jsonplaceholder.com api를 연동해서 데이터를 일정 시간마다 불러와서 H2에 저장해보자.

개발 환경 & API


순서


  1. Spring Batch 작성
  2. H2 연동
  3. Scheduler 추가

프로젝트 구조


과정


1. Config

블로그 예제 대부분이 JobBuilderFactory, StepBuilderFactory를 사용하는데 deprecated 되어서 현재 사용할 수 없다.

BatchConfig.java

package com.jv.batchbasic.config;


import com.jv.batchbasic.domain.Post;
import com.jv.batchbasic.repository.PostRepository;
import lombok.AllArgsConstructor;
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.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.json.JacksonJsonObjectReader;
import org.springframework.batch.item.json.builder.JsonItemReaderBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.UrlResource;
import org.springframework.transaction.PlatformTransactionManager;

import java.net.MalformedURLException;
import java.net.URL;

@Configuration
@AllArgsConstructor
@Slf4j
public class BatchConfig {

    private final PostRepository postRepository;

    @Bean
    public Job myJob(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws MalformedURLException {
        return new JobBuilder("myJob", jobRepository)
                .start(myStep( jobRepository, transactionManager))
                .build();
    }

    @Bean
    public Step myStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws MalformedURLException {
        return new StepBuilder("myStep", jobRepository)
                .<Post, Post>chunk(10, transactionManager)
                .reader(customItemReader())
                .writer(customItemWriter())
                .build();
    }

    @Bean
    public ItemReader<Post> customItemReader() throws MalformedURLException {
        String url = "https://jsonplaceholder.typicode.com/posts";
        URL resource = new URL(url);
        return new JsonItemReaderBuilder<Post>()
                .name("jsonReader")
                .resource(new UrlResource(resource))
                .jsonObjectReader(new JacksonJsonObjectReader<>(Post.class))
                .build();
    }

    @Bean
    public ItemWriter<Post> customItemWriter() {
        return items -> {
            for (Post post : items) {
                postRepository.save(post);
//                return; // return;을 추가하면 1분에 10개씩 가지고 온다.
            }
        };
    }
}

@Autowired 대신 생성자 주입으로 @AllArgsConstructor를 사용했다.
JobBuilder와 StepBuilder를 사용해 구현하고 JobRepository를 넣었다.
PlatformTransactionManager를 생략하면 에러가 발생한다.

ItemReader를 구현하지 않으면 에러가 발생한다. 읽고 출력해야 하므로 ItemReader, ItemWriter가 필요하다.
Reference
https://velog.io/@backtony/Spring-Batch-ItemReader

특정 url의 json객체를 받아오는 것이라 JacksonJsonObjectReader, new Url() 등이 사용되었다.

🔥@EnableBatchProcessing이 필수인가?

여기서 @EnableBatchProcessing 어노테이션을 추가하면 Batch가 작동되지도, 메타테이블이 생성되지도 않는다.
블로그들은 전부 이 과정을 필수라고 말했는데 JobBuilderFactory 방식을 쓰지 않아서일까? 하는 의문이 남는다.

2. Dto, Repository

Post.java

package com.jv.batchbasic.domain;

import jakarta.persistence.*;
import lombok.Getter;

@Entity
@Table
@Getter
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(length = 20, nullable = false)
    private Long idx; 

    @Column(length = 20, nullable = false)
    private Integer id;
    @Column(length = 20, nullable = false)
    private int userId;
    @Column(length = 100, nullable = false)
    private String title;
    @Column(length = 1000, nullable = false)
    private String body;
    
}

jsonplaceholder의 key값들을 잘 보고 iv들을 다 적어야 한다.

api iv들을 전부 적지 않으면 아래처럼 에러 발생.

PostRepository.java

package com.jv.batchbasic.repository;

import com.jv.batchbasic.domain.Post;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, Integer> {
}

3. property 설정 파일

여기까지가 Batch 기본이다. 이제 H2 연동을 해보자.
H2 연동 관련은 아래 설정으로 끝.
application.properties

# set h2 properties
spring.h2.console.enabled=true
# ?? ?? ?? ?? (true-?? ??, false-?? ??)
spring.datasource.generate-unique-name=false
# /h2-console? ????? ??? ?? ??
spring.h2.console.path=/h2-console

spring.datasouce.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=test

#jpa
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database=h2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

#logging
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate.sql=DEBUG
logging.level.jpa=DEBUG

결과 화면

좌측에 Spring Batch를 위한 메타 테이블들을 볼 수 있다. DB 연결 시 자동으로 생성된다.

4. Scheduler

BatchSceduler.java

package com.jv.batchbasic.config;


import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.*;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@AllArgsConstructor
@EnableScheduling
public class BatchScheduler {

    private final JobLauncher jobLauncher;
    private final Job myJob;

    @Scheduled(cron= "0 * * * * *")
    public void runJob() {
       JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
        jobParametersBuilder.addLong("currentTime", System.currentTimeMillis());

        try {
            jobLauncher.run(myJob, jobParametersBuilder.toJobParameters());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

스케줄러를 처음에 BatchConfig에 설정해야 한다고 생각했는데 아래 이슈로 실패했다.

따라서 별도의 스케줄러 클래스를 작성했고, 1분마다 실행되도록 Cron 문법을 사용했다. fixedRate는 서버 재시작마다 시간 영향을 받지만, cron은 일정하다.

마무리


더 심화된 공부가 필요하지만 가장 기초적인 예제로 좋았다.
간단한 테스트를 위해서는 JPA + H2 연동이 손쉽고 빠르다.

profile
Front-End와 Back-End 경험, 지식을 공유합니다.

0개의 댓글