주의 : 초보 프로그래머의 코드이므로 배경지식이 많이 부족합니다.

필자의 추후 작업시 참고 용도로 기술하는 포스팅이므로 코드의 개념적, 문법적 오류가 있을 수 있으며, 설명이 부족할 수 있으니 다른 전문 블로거의 글을 참고하길 바랍니다.

 

DB 테이블에서 SELECT 후 INSERT 하는 간단한 배치 프로그램을 만들고자한다.

프로젝트 순서는 다음과 같으며, 순서대로 진행해보도록하자.

작업제목 : 스프링(부트 포함) 배치 프로그램(SELECT 후 INSERT DB)
작업순서 : 
1. 스프링 부트 pom.xml 설정
2. BatchVO.class, BatchConfiguration.class, BatchJob.class, BatchApplication.class 생성
3. BatchConfiguration.class 작성
4. BatchVO.class 작성
5. BatchJob.class 작성
6. BatchApplication.class 작성
7. 정상 작동되는지 테스트 및 확인
8. jar 파일로 생성
9. bat 파일 생성 후 bat 파일로 실행
10. 윈도우 스케줄러에 등록하여 특정 시간에 bat 파일이 작동되도록 구성

작업 순서는 위와 같다.

이제 그럼 다음 포스팅부터는 본격적으로 작업 순서에 맞게 작업을 진행해보도록하자.

 

배치 파일(batch file)은 명령 인터프리터에 의해 실행되게끔 고안된 명령어들이 나열되어 있는 텍스트 파일이다. 배치 파일이 실행될 때, COMMAND.COM 또는 cmd.exe와 같은 셸 프로그램이 파일을 읽어 명령어를 줄 단위로 실행한다. 배치 파일은 보통 실행 파일을 자동으로, 연속적으로 실행할 때 유용하며 시스템 관리자가 따분한 일들을 자동화하기 위해 자주 사용한다.

오류내용 : [WARNING] The requested profile "pom.xml" could not be activated because it does not exist.
오류원인 : 메이븐 빌드시 Run configuration의 pom.xml 문제로 확인됨
오류처리 : Run configuration의 profile에서 pom.xml 삭제 후 정상적으로 메이븐 빌드됨

프로젝트 메이븐 빌드시 다음과 같은 오류가 발생하였다.

[WARNING] The requested profile "pom.xml" could not be activated because it does not exist.

해당 오류의 오류원인은 메이븐 빌드시 Run configuration의 pom.xml 문제로 확인되었다.

다음과 같이 이동해보자.

아래와 같이 표시된 부분의 pom.xml을 삭제해준다.

그리고 Run 버튼을 누르면

정상 빌드가 되면 다음과 같이 콘솔 창에서 확인할 수 있다.

다음과 같이 콘솔에서 확인 가능하다.

개념주제 : 스프링 부트 jar로 배포하기(maven, spring boot)
처리과정 : 
1. 프로젝트를 Run as > Maven Build로 한다.
2. goals에 package라고 제목을 입력한다.
3. 저장된 jar 파일을 cmd에서 빌드하여 정상 빌드되는지 확인한다.(필자의 경우 bat 파일로 생성)

필자의 경우 Boot 프로젝트를 jar 파일로 빌드하려고 하였으나 빌드 후 cmd에서 정상적으로 구동이 안되는 문제가 발생하였다.

 

이로인해 여러 방법을 찾았고 문제를 해결하여 해결 방법을 기술하고자한다.

우선 다음과 같이 프로젝트를 Maven 빌드해준다.

아래와 같이 입력해주고 Run을 클릭하여 빌드한다.

만약 정상적으로 빌드가 안될 경우에는 workspace에서 프로젝트를 추가해준 후 다시 한번 시도를 해봐야 한다.

 

빌드가 정상적으로 완료되면 다음과 같이 BUILD SUCCESS 문구를 확인할 수 있다.

그리고 빌드된 파일의 경로는 콘솔 위를 보면 다음과 같이 경로를 확인할 수 있다.

정상적으로 빌드가 되었으면 cmd에서 해당 jar 파일을 실행하여 정상 구동되는지 확인을 하면된다.

java -jar ./springBatch.jar

pause

필자의 경우 .bat 파일로 만들었으며 해당 파일을 구동하면 해당 jar 파일이 구동되도록 만들었다.

다음과 같이 정상 구동되는 것을 확인할 수 있다.

 

스프링/스프링부트 어노테이션
어노테이션 설명/예제
@NoArgsConstructor

<설명>

파라미터가 없는 default 생성자를 생성해주는 어노테이션

 

<예제>

package com.bootbatch.main;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class UserVO {
private int count;


public UserVO(int count) {
this.count = count;
}
}

@Configuration

<설명>

스프링의 @Configuration 어노테이션은 이 클래스가 구성 클래스임을 스프링에 알린다.

스프링은 @Configuration이 달린 클래스를 보면 일단 그 안에서 빈 인스턴스 정의부, 즉 @Bean을 붙인(빈 인스턴스를 생성해 반환하는) 자바 메서드를 찾는다.

 

아래 <예제1>에서 dataSource method를 보자 @Bean처리된 것을 확인할 수 있다.

<예제2>를 보면 private final로 dataSource 변수에 DI(Dependency Injection)가 된 것을 확인할 수 있다.

 

@ComponentScan

<설명>

@ComponentScan 어노테이션은 @Component 어노테이션 및 streotype(@Service, @Repository, @Controller) 어노테이션이 부여된 Class들을 자동으로 Scan하여 Bean으로 등록해주는 역할을 하는 어노테이션이다.

@PropertySource

<설명>

Property 파일을 Environment로 로딩할 수 있다.

@EnableBatchProcessing 

<설명>

@EnableBatchProcessing 어노테이션은 다른 스프링의 Enable**과 비슷하게 동작하며 배치 Job을 구성하는 기본 설정을 제공한다.

<예제1>

@Configuration
@ComponentScan("com.bootbatch")
@PropertySource("classpath:/database.properties")
@EnableBatchProcessing
public class BatchConfiguration {

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(env.getRequiredProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));
        return dataSource;
    }

    @Bean
    public DataSourceInitializer databasePopulator() {
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        populator.addScript(new ClassPathResource("org/springframework/batch/core/schema-oracle10g.sql"));
        populator.addScript(new ClassPathResource("total_count.sql"));
        populator.setContinueOnError(true);
        populator.setIgnoreFailedDrops(true);

        DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDatabasePopulator(populator);
        initializer.setDataSource(dataSource());
        return initializer;
    }

}

<예제2>

package com.bootbatch.job;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.bootbatch.main.BatchVO;

import lombok.RequiredArgsConstructor;


@Configuration
@RequiredArgsConstructor
public class BatchJob {
private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactoy;
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;


    @Bean
    public Job jdbcCursorItemReaderJob() {
     return jobBuilderFactory.get("jdbcCursorItemReaderJob")
     .start(jdbcCursorItemReaderStep())
     .build();
    }

    @Bean
    public Step jdbcCursorItemReaderStep() {
     return stepBuilderFactoy.get("jdbcCursorItemReaderStep")
     .<BatchVO, BatchVO>chunk(10)
     //reader에서 받아서
     .reader(jdbcCursorItemReader())
     //writer에 넣는다.
     .writer(jdbcCursorItemWriter())
     .build();
    }


    @Bean
    JdbcBatchItemWriter jdbcCursorItemWriter(){
     JdbcBatchItemWriter itemWriter = new JdbcBatchItemWriter();
     itemWriter.setDataSource(dataSource);
     itemWriter.setSql("insert into insert_count values(:count)");
     itemWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider());
     return itemWriter;
    }
    
    @Bean
    public JdbcCursorItemReader jdbcCursorItemReader(){
     System.out.println("===>jdbcCursorItemReader 접속!!");
     return new JdbcCursorItemReaderBuilder()
     .fetchSize(10)
     .dataSource(dataSource)
     .rowMapper(new BeanPropertyRowMapper<>(BatchVO.class))
     .sql("SELECT COUNT FROM TOTAL_COUNT")
     .name("jdbcCursorItemWriter")
     .build();
 
    }
}

@Entity

@Entity

- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 합니다.
- @Entity가 붙은 클래스는 JPA가 관리하고, 엔티티라 부릅니다.
- @Entity 적용 시 주의사항
- 기본 생성자가 필수(파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스에는 사용 불가
- 저장할 필드에 final 사용 불가

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts