ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [퍼옴] [Spring Batch] 스프링 배치 강좌 5. JDBC 로 디비 읽어서 CSV 파일에 쓰기
    spring 2021. 4. 14. 15:26
    728x90
    반응형

    목적

    성적 데이터를 저장하는Exam테이블 데이터를 읽어서 시험 점수0-100점스케일을A-F스케일로 변환하여CSV파일에 저장한다.

    Inputsrc/test/resourcescom/fwantastic/example4 *폴더를 생성 후 *create-table.sql를 만든다.

    Exam테이블을 생성 하고 테스트 데이터를 넣는다.Exam테이블에는 시험 점수가 0-100점 스케일로 저장되어있다.

    create-table.sql

    CREATE TABLE EXAM (
        CLASS_NAME      VARCHAR2(255)
        , STUDENT_NAME  VARCHAR2(255)
        , SCORE         INT
        , GRADE         VARCHAR2(1)
    )
    ;
    
    INSERT INTO EXAM
    (CLASS_NAME, STUDENT_NAME, SCORE) VALUES
    ('MATH'    , 'Fwantastic', 64   )
    ;
    
    INSERT INTO EXAM
    (CLASS_NAME, STUDENT_NAME, SCORE) VALUES
    ('MATH'    , 'IU'        , 95   )
    ;
    
    INSERT INTO EXAM
    (CLASS_NAME        , STUDENT_NAME, SCORE) VALUES
    ('COMPUTER SCIENCE', 'Fwantastic', 100  )
    ;
    
    INSERT INTO EXAM
    (CLASS_NAME        , STUDENT_NAME, SCORE) VALUES
    ('COMPUTER SCIENCE', 'IU'        , 86   )
    ;
    
    INSERT INTO EXAM
    (CLASS_NAME        , STUDENT_NAME, SCORE) VALUES
    ('COMPUTER SCIENCE', '수지'       , 77   )
    ;

    Exam.java

    **Exam 테이블 데이터를 매핑 *할 *자바 클래스를 만든다. com.fwantastic.example4 패키지에 아래 클래스를 추가하자.

    Exam.java**

    package com.fwantastic.example4;
    
    import java.util.Arrays;
    import java.util.Comparator;
    
    import lombok.Getter;
    import lombok.Setter;
    
    @Getter
    @Setter
    public class Exam {
        private String className;
        private String studentName;
    
        // 시험 점수. 0-100점
        private double score;
    
        // 시험 점수 스케일. A-F
        private String grade;
    
        @Getter
        public static enum Grade {
            A("A", 90), B("B", 80), C("C", 70), D("D", 60), F("F", 0);
    
            private final String grade;
            private final double score;
    
            private Grade(String grade, double score) {
                this.grade = grade;
                this.score = score;
            }
    
            public static String convertScore(double score) {
                return Arrays.stream(values()).filter(grade -> grade.score < score)
                        .max(Comparator.comparing(grade -> grade.score)).get().getGrade();
            }
        }
    }

    ****시험 점수 grade 스케일은 다음과 같다.


    • A = 90점 이상
    • B = 80점 이상
    • C = 70점 이상
    • D = 60점 이상
    • F = 60점 이하

    Getter/Setter는 lombok을 사용하여 처리했는데 셋업이 안되있다면 아래 포스트를 참고하자.

    자바 lombok 설치와 Getter/Setter 간편하게 만들기


    ExamProcessor.java

    *시험 점수를 grade 스케일로 변환 한 값을 Exam 객체에 매핑 후 반환한다. *com.fwantastic.example4 패키지에 아래 클래스를 추가하자.

    ExamProcessor.java****

    package com.fwantastic.example4;
    
    import org.springframework.batch.item.ItemProcessor;
    
    /**
     * 시험 점수 0-100점 스케일을 A-F 스케일로 변환한다.
     */
    public class ExamProcessor implements ItemProcessor<Exam, Exam> {
    
        public Exam process(Exam exam) throws Exception {
            exam.setGrade(Exam.Grade.convertScore(exam.getScore()));
            return exam;
        }
    
    }

    job4.xml

    ****src/main/resources/com/fwantastic/example4에 아래의 **job4.xml을 만든다.

    자세한 설명은 주석을 참고하자.

    job4.xml**

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:batch="http://www.springframework.org/schema/batch"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:jdbc="http://www.springframework.org/schema/jdbc"
        xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
      http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    
        <import resource="classpath:/common-context.xml" />
    
        <!-- job을 시작하기 전에 Exam 테이블과 테스트 데이터 생성. -->
        <jdbc:initialize-database
            data-source="dataSource">
            <jdbc:script
                location="classpath:com/fwantastic/example4/create-table.sql" />
        </jdbc:initialize-database>
    
        <job id="myJob4"
            xmlns="http://www.springframework.org/schema/batch">
    
            <description>
            JDBC로 디비를 읽어서 CSV 파일로 저장하는 예제.
            </description>
    
            <step id="myStep1">
                <tasklet>
                    <chunk reader="examReader"
                        processor="examProcessor" writer="examWriter"
                        commit-interval="10" />
                </tasklet>
            </step>
        </job>
    
        <bean id="examReader"
            class="org.springframework.batch.item.database.JdbcCursorItemReader">
            <property name="dataSource" ref="dataSource" />
            <property name="sql">
                <value>
                    <![CDATA[
                    SELECT CLASS_NAME, STUDENT_NAME, SCORE
                    FROM EXAM
                    ORDER BY CLASS_NAME, STUDENT_NAME, SCORE
                    ]]>
                </value>
            </property>
            <property name="rowMapper" ref="examRowMapper" />
        </bean>
    
        <!-- JDBC reader로 읽은 데이터를 자바 객체로 매핑 해준다. -->
        <bean id="examRowMapper"
            class="org.springframework.jdbc.core.BeanPropertyRowMapper"
            scope="step">
            <property name="mappedClass"
                value="com.fwantastic.example4.Exam" />
        </bean>
    
    
        <!-- Exam 데이터 가공 -->
        <bean id="examProcessor"
            class="com.fwantastic.example4.ExamProcessor" scope="step" />
    
    
        <bean id="examWriter"
            class="org.springframework.batch.item.file.FlatFileItemWriter"
            scope="step">
    
            <!-- Aggregate이 모으다 라는 뜻인데, 한 라인을 어떻게 쓸지 설정한다. -->
            <!-- 여기선 콤마로 필드를 구분하고 className, studentName, grade 순서대로 값을 쓰라고 설정했다. -->
            <property name="lineAggregator">
                <bean
                    class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
                    <property name="delimiter" value="," />
                    <property name="fieldExtractor">
                        <bean
                            class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
                            <property name="names"
                                value="className, studentName, grade"></property>
                        </bean>
                    </property>
                </bean>
            </property>
    
            <!-- 쓸 파일 위치를 지정해준다. 배치가 종료되면 프로젝트 root 레벨 (pom.xml과 같은 위치) 에 output
                폴더가 생성된다. -->
            <property name="resource">
                <bean
                    class="org.springframework.core.io.FileSystemResource">
                    <constructor-arg name="path"
                        value="output/com/fwantastic/example4/output.csv" />
                </bean>
            </property>
    
            <!-- 파일이 존재하면 삭제하고 새로 만든다. -->
            <property name="shouldDeleteIfExists" value="true" />
        </bean>
    
    </beans>

    테스트

    **src/test/java에 **com.fwantastic.example4 패키지 만들고 아래 junit 클래스를 추가하자.

    MyJobTest.java

    package com.fwantastic.example4;
    
    import java.io.File;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.batch.core.ExitStatus;
    import org.springframework.batch.core.JobExecution;
    import org.springframework.batch.test.JobLauncherTestUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "/com/fwantastic/example4/job4.xml" })
    public class MyJobTest {
    
        @Autowired
        private JobLauncherTestUtils jobLauncherTestUtils;
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        private static AtomicBoolean isLaunched = new AtomicBoolean(false);
    
        private JobExecution jobExecution;
    
        @Before
        public void setUp() throws Exception {
            if (!isLaunched.getAndSet(true)) {
                jobExecution = jobLauncherTestUtils.launchJob();
            }
        }
    
        @Test
        public void testExitCode() {
            Assert.assertEquals(ExitStatus.COMPLETED.getExitCode(), jobExecution.getExitStatus().getExitCode());
        }
    
        // output 파일이 생성되었는지 확인한다. 테스트는 더 디테일하면 할수록 더 좋다.
        @Test
        public void testOutputFileCreated() {
            File file = new File("output/com/fwantastic/example4/output.csv");
            Assert.assertTrue(file.exists());
        }
    }

    ****실행 결과****

    프로젝트 root 레벨에 output/com/fwantastic/example4/output.csv 파일이 생성된 것을 확인 할 수 있다.

    COMPUTER SCIENCE,Fwantastic,A
    COMPUTER SCIENCE,IU,B
    COMPUTER SCIENCE,수지,C
    MATH,Fwantastic,D
    MATH,IU,A
    
    728x90
    반응형
Designed by Tistory.