Notice
Recent Posts
Recent Comments
Link
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags more
Archives
Today
Total
관리 메뉴

두리공장

[spring batch] 스프링 부트 프로젝트에 스프링 배치 이식하기 본문

java

[spring batch] 스프링 부트 프로젝트에 스프링 배치 이식하기

두리공장 2022. 7. 18. 00:34

스프링 부트로 개발된 back-end 에 스프링 배치를 이식해야 할 경우가 생겼다.

이렇게 하려면, 아래와 같은 순서로 셋팅해야 한다.
1. pom.xml 에 의존성 추가 (dbcp 포함)
2. application.yml 에 db connection 정보 추가
3. DataSourceConfig, VO 추가
4. 시작 지점에 스케줄러 추가
5. JobConfig 구현 (Job, Step, tasklet, ItemReader, ItemWriter)...

순서대로 셋팅해 보자
pom.xml 에 의존성 추가 (dbcp 포함)

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>


2. application.yml 에 db connection 정보 추가

  postgres:
    jdbc-url: jdbc:postgresql://localhost:5432/${db schema}
    username: ${id}
    password: ${password}
    driver-class-name: org.postgresql.Driver


3. DataSourceConfig, VO 추가

package com.sunnier.api.batch;

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Bean(name="postgres")
    @Primary
    @ConfigurationProperties("spring.postgres")
    public DataSource postgres(){
        DataSourceBuilder builder = DataSourceBuilder.create();
        builder.type(HikariDataSource.class);
        return builder.build();
    }
//    @Value("${spring.datasource.driver-class-name}")
//    private String postgresDriverClassName;
//
//    @Value("${spring.datasource.url}")
//    private String postgresJJdbcUrl;
//
//    @Value("${spring.datasource.username}")
//    private String postgresJdbcUsername;
//
//    @Value("${spring.datasource.password}")
//    private String postgresJdbcPassword;
//
//    @Bean(name="postgres")
//    @Primary
//    public DataSource postgresDataSource() {
//        return getDataSource(postgresDriverClassName,postgresJJdbcUrl, postgresJdbcUsername, postgresJdbcPassword);
//    }
//    public DataSource getDataSource(String externalDriverClassName, String externalJdbcUrl, String externalJdbcUsername, String externalJdbcPassword){
//        BasicDataSource dataSource = new BasicDataSource();
//        dataSource.setDriverClassName(externalDriverClassName);
//        dataSource.setUrl(externalJdbcUrl);
//        dataSource.setUsername(externalJdbcUsername);
//        dataSource.setPassword(externalJdbcPassword);
//        return dataSource;
//    }
}
package com.sunnier.api.batch;

import lombok.Data;

@Data
public class CustomerVO {
    private String id;
    private String name;
    private String countryname;
}


4. 시작 지점에 스케줄러 추가

@SpringBootApplication
@EnableScheduling
public class SunnierApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(SunnierApiApplication.class, args);
    }


    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job job;

    //잡을 실행한다.
    @Scheduled(cron = "0 0/1 * * * *")
    public void jobExecute() throws Exception {
        if(jobLauncher == null){
            jobLauncher = new SimpleJobLauncher();
        }

        JobParameters param = new JobParametersBuilder().addString("JobId", String.valueOf(System.currentTimeMillis())).toJobParameters();
        System.out.println("server IP : " + ip);
        if(ip.equals("192.168.0.71")) {
            System.out.println("=======start job========");
            jobLauncher.run(job, param);
            System.out.println("=======end job========");
        }
    }
}


5. JobConfig 구현 (Job, Step, tasklet, ItemReader, ItemWriter)...

package com.sunnier.api.batch;

import com.sunnier.api.portal.freeboard.service.BoardService;
import com.sunnier.api.portal.freeboard.vo.BoardVo;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.adapter.ItemWriterAdapter;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
import org.springframework.batch.item.support.CompositeItemWriter;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Configuration
@EnableBatchProcessing
public class JobConfig {
    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private BoardService boardService;


    @Bean
    public Job queryExecuteJob() throws Exception{
        return this.jobBuilderFactory.get("queryExecuteJob")
                .start(queryExecuteStep())
                .next(callKakaoStep())
                .build();
    }


    //쿼리를 실행한다.(tasklet)
    @Bean
    public Step queryExecuteStep(@Qualifier("postgres")DataSource dataSource){
         String sql = "insert into COUNTRIES_BATCH " +
                "select " +
                "* " +
                "from COUNTRIES " +
                "where NAME like 'H%'";

        return this.stepBuilderFactory.get("queryExecuteStep")
                .tasklet(((stepContribution, chunkContext) -> {
                    System.out.println("Hello, world");
                    List<BoardVo> list = boardService.findAll();
                    System.out.println("데이터를 조회해 온다." + list.get(0).getTitle());
                    // select insert 쿼리를 실행한다.
                    new JdbcTemplate(dataSource).execute(sql);
                    return RepeatStatus.FINISHED;
                }))
                .build();
    }
    //데이터를 읽은다음, 카카오 알림톡으로 메시지를 발송하고, 결과를 업데이트 한다.
    public Step callKakaoStep() throws Exception{
        return this.stepBuilderFactory.get("callKakaoStep")
                .chunk(10)
                .reader(tableItemReader(null))
                .writer(compositeItemWriter())
                .build();
    }

    //데이터 리더 구현
    @Bean
    public JdbcCursorItemReader<CustomerVO> tableItemReader(@Qualifier("postgres")DataSource dataSource) throws Exception {
        String sql = "select id,name,countryname from customer_large";
        return new JdbcCursorItemReaderBuilder<CustomerVO>()
                .name("tableItemReader")
                .dataSource(dataSource)
                .rowMapper(new BeanPropertyRowMapper<>(CustomerVO.class))
                .sql(sql)
                .build();
    }

    //데이터 라이터 구현
    @Bean
    public CompositeItemWriter compositeItemWriter() throws Exception {
        final CompositeItemWriter<CustomerVO> compositeItemWriter = new CompositeItemWriter<>();
        compositeItemWriter.setDelegates(Arrays.asList(
                callKakaoApiWriter(null),
                printItemWriter()));
        return compositeItemWriter;
    }

    //카카오 알림톡 발송용 라이터 구현
    @Bean
    public ItemWriter<CustomerVO> callKakaoApiWriter(KakaoAPIService apiService) throws Exception {
        ItemWriterAdapter<CustomerVO>  itemWriterAdapter = new ItemWriterAdapter<>();
        itemWriterAdapter.setTargetObject(apiService);
        itemWriterAdapter.setTargetMethod("SendMessage");
        return itemWriterAdapter;
    }

    //카카오 알림톡 발송을 위한 샘플 클래스
    @Service
    public class KakaoAPIService{

        public Map<String, Object> SendMessage(){
            Map<String, Object> result = new HashMap<>();
            result.put("message", "OK");
            return result;
        }
    }

    //화면 출력을 위한 라이터
    public ItemWriter<CustomerVO> printItemWriter() throws Exception {
        return new ItemWriter<CustomerVO>() {
            @Override
            public void write(List<? extends CustomerVO> list) throws Exception {
                for(CustomerVO item : list){
                    System.out.println("item result =======> " + item.getCountryname());
                }
            }
        };
    }

    //카카오 알림톡 발송후 결과 업데이터 라이터 구현



}

끝.