ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Batch] 이제 LocalDate도 지원돼요!
    웹 개발/Spring Framework 2023. 9. 2. 16:50

    많은 블로그를 보면 스프링 배치에서 LocalDateTime, LocalDate, LocalTime 등이 jobParameters로 지원되지 않아서 String을 파싱하는 방법으로 알려준다.

     

    이전에는 java 8의 time api를 지원하지 않아서 String을 파싱하는 방법 밖에 없었다. 그 이후로 최신 글이 없다보니 지금의 개발자들도 String으로 파싱해서 사용하고 있지 않을까해서 포스팅 하기.

     

    스프링 배치에서 java 8의 time api를 2022년 10월부터 지원하고 있다.

    참고글 : https://github.com/spring-projects/spring-batch/issues/1035

     

    테스트 해봅시다.

     

    준비물

    - 버전: 스프링 배치 5.0.0 이상, 스프링 부트 배치 3.0 이상(아직까지 스프링 부트 2.x 버전을 많이 사용해서 바로 적용은 힘들지도)

    - 스프링 배치를 돌리기 위한 밑작업(스프링 배치가 사용하는 테이블 생성, db 연동, 같은 jobParameter로 n번 돌리기 위한 incrementer)


    테스트 시작

    참고용으로 내가 사용한 커스텀 incrementer

    @Configuration
    class CurrentTimeIncrementer : JobParametersIncrementer {
    
        override fun getNext(parameters: JobParameters?): JobParameters {
            return JobParametersBuilder()
                .addLong("currentTimeMillis", System.currentTimeMillis())
                .toJobParameters()
        }
    }

     

    application.yaml

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/test
        driverClassName: com.mysql.cj.jdbc.Driver
      batch:
        job:
          name: ${job.name:NONE}

     

    build.gradle.kts 추가한 의존성(로컬에 이미 세팅이 되어있는 mysql을 사용했다)

    implementation("org.springframework.boot:spring-boot-starter-batch")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    runtimeOnly("com.mysql:mysql-connector-j")

     

    SampleJobConfiguration.kt

    @Configuration
    class SampleJobConfiguration(
        private val transactionManager: PlatformTransactionManager,
        private val jobRepository: JobRepository,
    ) {
    
        @Bean
        fun sampleJob(sampleStep: Step): Job = JobBuilder("sample-job", jobRepository)
            .incrementer(CurrentTimeIncrementer())
            .flow(sampleStep)
            .end()
            .build()
    
        @Bean
        @JobScope
        fun sampleStep(
            sampleReader: ItemReader<LocalDateTime>,
            sampleWriter: ItemWriter<LocalDateTime>
        ): Step {
    
            return StepBuilder("sampleStep", jobRepository)
                .chunk<LocalDateTime, LocalDateTime>(10, transactionManager)
                .reader(sampleReader)
                .writer(sampleWriter)
                .build()
        }
    
        @Bean
        @StepScope
        fun sampleReader(
            // 주목!!
            @DateTimeFormat(pattern = "yyyy-MM-ddHH:mm")
            @Value("#{jobParameters[targetDateTime]}")
            targetDateTime: LocalDateTime,
    
            @DateTimeFormat(pattern = "yyyy-MM-dd")
            @Value("#{jobParameters[targetDate]}")
            targetDate: LocalDate,
    
            @DateTimeFormat(pattern = "HH:mm")
            @Value("#{jobParameters[targetTime]}")
            targetTime: LocalTime,
        ): ItemReader<LocalDateTime> {
            return ListItemReader(
                listOf(
                    targetDateTime,
                    targetDate.atTime(12, 0),
                    targetTime.atDate(targetDate)
                )
            )
        }
    
        @Bean
        @StepScope
        fun sampleWriter(): ItemWriter<LocalDateTime> {
            val itemWriter = ItemWriter<LocalDateTime> {
                println(">>>>>>> parameters: ${it.items}")
            }
    
            return itemWriter
        }
    }

     

    LocalDateTime은  yyyy-MM-dd`T`HH:mm, LocalDate는 yyyy-MM-dd, LocalTime은 HH:mm 패턴을 지키면 DateTimeFormat이 없더라도 알아서 변환된다. 다른 패턴을 사용하고 싶다면 @DateTimeFormat이 필요하다.

    (암묵적으로 패턴을 지키면 변환되더라도 개발자의 실수를 줄이기 위해 @DateTimeFormat을 선언해두고 사용하는게 좋을듯 싶다.)

     

    커맨드 라인으로 실행할 경우다. 

     ./gradlew bootRun --args='--job.name=sample-job targetDateTime=2023-09-0209:00 targetDate=2023-09-02 targetTime=15:00'

     

    인텔리J를 사용하고 있다면, 아래처럼 설정하면 된다.

     

    결과

    잘 변환되었다.


    주의할 점

    1. jobParameters로 넣을 때 띄어쓰기를 사용하면 인식하지 못하기 때문에 값을 붙여써줘야한다.

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
    @Value("#{jobParameters[targetDateTime]}")
    targetDateTime: LocalDateTime,

    로 수정 후,  아래처럼 커맨드 실행했을 때

     ./gradlew bootRun --args='--job.name=sample-job targetDateTime=2023-09-02 09:00 targetDate=2023-09-02 targetTime=15:00'

     

    2023-09-02까지만 인식

     

    2. 항상 jobParameters의 값을 넣지 않는다면 nullable로 쓰자.

    나의 경우, 잡이 돌아가는 시간을 기준으로 파라미터를 사용하는데 가끔씩 잡이 실패하거나 잘못 돌아갔을 때, 원하는 파라미터를 넣어서 실행하려고 jobParameters를 사용한다.

    Not null로 선언해두면 jobParameters를 입력하지 않으면 non-null 에러가 난다.

     

     

Designed by Tistory.