감 잃지말고 개발하기

[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #8. 데이터 수정하기 (3) (마지막) 본문

JSP/MVC

[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #8. 데이터 수정하기 (3) (마지막)

persii 2023. 6. 30. 18:02

지난 포스팅 "도서 사이트에서 다중 카테고리 구현하기 #7. 데이터 수정하기 (2)"에서 도서 정보를 수정하는 로직 중, 다중 트랜잭션을 처리하는 메서드를 정리해 보았다.

이번 포스팅에서는 이 메서드 코드를 가독성 있게 정리해 보도록 하겠다.

 


♠ 이전 포스팅이 궁금하면 아래 링크를 클릭하세요 ♠

2023.06.29 - [JSP/MVC] - [JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #7. 데이터 수정하기 (2)

 

[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #7. 데이터 수정하기 (2)

지난 포스팅 "도서 사이트에서 다중 카테고리 구현하기 #6. 데이터 수정하기 (1)" 코드에 이어 그 뒷부분, 이번 구현 과정의 핵심 다중 트랜잭션 처리 메서드를 정리해 보도록 하겠다. ※ 참고 ※

persimmon-ary-stepbystep.tistory.com


 

목표

♠ 트랜잭션의 ACID 원칙을 준수하여 데이터베이스 작업을 수행할 수 있다.

♠ 중복된 코드를 최소화하고 가독성 있는 코드를 지향한다.

 

 

기존 메서드 수정

기존 메서드에서 중복되는 코드를 최소화고 가독성 있는 코드로 수정해 보자. 

1. DML 구문작업 → 메서드화

기존 메서드를 살펴보면 if문 안에 try-with-resources문을 이중삼중으로 사용하면서 DML 작업을 수행하도록 하고 있다. 

이것을 모두 메서드화하여 밖으로 빼준다.

1-1. Book 객체를 UPDATE 하는 트랜잭션 메서드

public int updateBookDetails(Book book) throws SQLException {
    int updateCount = 0;
    String update_sql = "UPDATE jspbookshop.book "
                    + "SET b_name=?, b_writer=?, b_translator=?, b_publisher=?, b_bc_code=?, "
                    + "b_price=?, b_image=?, b_page=?, b_publish_date=?, b_content=? "
                    + "where b_id=?";

    try(PreparedStatement pstmt = conn.prepareStatement(update_sql)) {
        pstmt.setString(1, book.getB_name());
        pstmt.setString(2, book.getB_writer());
        pstmt.setString(3, book.getB_translator());
        pstmt.setString(4, book.getB_publisher());
        pstmt.setInt(5, book.getB_bc_code());
        pstmt.setInt(6, book.getB_price());
        pstmt.setString(7, book.getB_image());
        pstmt.setInt(8, book.getB_page());
        pstmt.setString(9, book.getB_publish_date());
        pstmt.setString(10, book.getB_content());
        pstmt.setInt(11, book.getB_id());

        updateCount = pstmt.executeUpdate();
    } catch (SQLException e) {
        System.out.println(" updateBookDetails ERROR1 pstmt 처리 도중 에러 : "+e);
    } 
    return updateCount;
}

 

1-2. 도서 아이디에 따른 도서 서브 카테고리를 SELECT 하는 DQL 트랜잭션 메서드

public List<Integer> getExistingSubcategories(int b_id) throws SQLException {
    List<Integer> existingSubcategoriesList = new ArrayList<>();
    String select_sql = "SELECT bsc_id FROM jspbookshop.booksubcatgy WHERE bsc_b_id = ?";

    try (PreparedStatement subSelectPstmt = conn.prepareStatement(select_sql)) {
        subSelectPstmt.setInt(1, b_id);

        try(ResultSet rs = subSelectPstmt.executeQuery()) {
            while (rs.next()) {
                existingSubcategoriesList.add(rs.getInt(1));
            } 
        } catch (SQLException e) {
            System.out.println(" getExistingSubcategories ERROR2 rs 처리 도중 에러 : "+e);
        }	
    } catch (SQLException e) {
        System.out.println(" getExistingSubcategories ERROR1 subSelectPstmt 처리 도중 에러 : "+e);
    }
    System.out.println("existingSubcategoriesDB : "+existingSubcategoriesList.size());
    return existingSubcategoriesList;
}

 

1-3. 도서 서브 카테고리 배열을 INSERT하는 DML 트랜잭션 메서드

public int insertSubCatgys(int startCount, 
			    int limitCount, 
                            int mod_arr[], 
                            int b_id) throws SQLException {
    int updateCount = 0;
    String insert_sql = "INSERT INTO jspbookshop.booksubcatgy(bsc_b_id, bsc_bc_code) VALUES(?, ?)";

    try(PreparedStatement subInsertPstmt = conn.prepareStatement(insert_sql)) {
        subInsertPstmt.setInt(1, b_id);
        for(int i=startCount; i<limitCount; i++) {
            subInsertPstmt.setInt(2, mod_arr[i]);
            updateCount += subInsertPstmt.executeUpdate();
        }
    } catch (SQLException e) {
        System.out.println(" insertSubCatgys ERROR1 subInsertPstmt 처리 도중 에러 : "+e);
    }
    return updateCount;
}

 

1-4. 도서 서브 카테고리 배열을 UPDATE하는 DML 트랜잭션 메서드

public int updateSubCatgys(List<Integer> catgyIdList, 
			    int limitCount, 
                            int mod_arr[], 
                            int b_id) throws SQLException {
    int updateCount = 0;
    String update_sql = "UPDATE jspbookshop.booksubcatgy "
            + "SET bsc_bc_code = ? "
            + "WHERE bsc_b_id = ? "
            + "AND bsc_id = ?";

    try(PreparedStatement subUpdatePstmt = conn.prepareStatement(update_sql)) {
        for(int i=0; i<limitCount; i++) {
            subUpdatePstmt.setInt(1, mod_arr[i]);
            subUpdatePstmt.setInt(2, b_id);
            subUpdatePstmt.setInt(3, catgyIdList.get(i));
            updateCount += subUpdatePstmt.executeUpdate();
        }
    } catch (SQLException e) {
        System.out.println(" updateSubCatgys ERROR1 subUpdatePstmt 처리 도중 에러 : "+e);
    }
    return updateCount;
}

 

1-5. 도서 서브 카테고리 배열을 DELETE하는 DML 트랜잭션 메서드

public int deleteSubCatgys(List<Integer> catgyIdList, 
			    int startCount, 
                            int limitCount, 
                            int b_id) throws SQLException {
    int updateCount = 0;
    String delete_sql = "DELETE FROM jspbookshop.booksubcatgy "
                    + "WHERE bsc_id = ? "
                    + "AND bsc_b_id = ?";

    try(PreparedStatement subDeletePstmt = conn.prepareStatement(delete_sql)) {
        for(int i=startCount; i<limitCount; i++) {
            subDeletePstmt.setInt(1, catgyIdList.get(i));
            subDeletePstmt.setInt(2, b_id);
            updateCount += subDeletePstmt.executeUpdate();
        }
    } catch (SQLException e) {
        System.out.println(" deleteSubCatgys ERROR1 subDeletePstmt 처리 도중 에러 : "+e);
    }

    return updateCount;
}

 

2. if-else if문 → switch-case문

기존 메서드를 살펴보면 경우 1. 의 if문 안에 보기 더러운 if-else if문이 들어가 있음을 볼 수 있다. 

가독성을 위해 조건문을 switch-case문으로, 기존의 DML 작업을 위에서 작성한 메서드로 변경해 준다.

 

if (existingSubcategoriesDB > 0 && updatedSubcategories > 0) {
    // 기존의 서브 분류(DB) 데이터 및 변경할 서브 분류 데이터가 있는 경우 
    System.out.println("기존의 서브 분류(DB) 데이터 및 변경할 서브 분류 데이터가 있습니다.");
    int result = updatedSubcategories-existingSubcategoriesDB;
    int DMLcase = 0;
    if(result > 0) {
        DMLcase = 1;
    } else if (result < 0) {
        DMLcase = -1;
    } 

    /* 기존의 서브 분류(DB) 기준으로 DML 처리 */
    switch (DMLcase) {
    case 1:
        /* 변경할 서브 분류 데이터가 더 많은 경우 
         * 1. 기존의 서브 분류(DB) 개수만큼 수정 
         * 2. (변경할 서브 분류 데이터 길이 - 기존의 서브 분류(DB) 개수)만큼 INSERT */ 
        System.out.println("변경할 서브 분류 데이터가 더 많은 경우");
        updateCount = updateSubCatgys(existingSubcategoriesList, 
        				existingSubcategoriesDB, 
                                        sub_catgy_arr, 
                                        book.getB_id());
        updateCount += insertSubCatgys(existingSubcategoriesDB, 
        				updatedSubcategories, 
                                        sub_catgy_arr, 
                                        book.getB_id());
        break;

    case -1:
        /* 기존의 서브 분류(DB) 데이터가 더 많은 경우
         * 1. 변경할 서브 분류 데이터 길이만큼 수정
         * 2. 남은 기존 서브 분류(DB) 데이터 DELETE */
        System.out.println("기존의 서브 분류(DB) 데이터가 더 많은 경우");
        updateCount = updateSubCatgys(existingSubcategoriesList, 
        				updatedSubcategories, 
                                    	sub_catgy_arr, 
                                    	book.getB_id());
        updateCount += deleteSubCatgys(existingSubcategoriesList, 
        				updatedSubcategories, 
                                        existingSubcategoriesDB, 
                                        book.getB_id());
        break;

    default:
        /* 데이터 개수가 동일한 경우 */
        System.out.println("데이터 개수가 동일한 경우");
        updateCount = updateSubCatgys(existingSubcategoriesList, 
        				updatedSubcategories, 
                                        sub_catgy_arr, 
                                        book.getB_id());
        break;
    }
}

else if(existingSubcategoriesDB > 0 && updatedSubcategories <= 0 ) {
    // 기존의 서브 분류(DB) 데이터가 있지만 변경할 서브 분류 데이터가 없는 경우
    System.out.println("기존의 서브 분류(DB) 데이터가 있지만 변경할 서브 분류 데이터가 없습니다.");
    updateCount = deleteSubCatgys(existingSubcategoriesList, 
    					0, 
                                    existingSubcategoriesDB, 
                                    book.getB_id());
}

else if(existingSubcategoriesDB == 0 && updatedSubcategories > 0) {
    // 기존의 서브 분류(DB) 데이터는 없지만 변경할 서브 분류 데이터는 있는 경우
    System.out.println("기존의 서브 분류(DB) 데이터는 없지만 변경할 서브 분류 데이터가 있습니다.");
    updateCount = insertSubCatgys(0, 
    				    updatedSubcategories, 
                                    sub_catgy_arr, 
                                    book.getB_id());
}

else {
    System.out.println("기존의 서브 분류(DB) 및 변경할 서브 분류 데이터가 없습니다.");
    updateCount = 1;
}

 

전체 코드

/** 책 정보 수정 메서드 */
public int updateBook(Book book, int[] sub_catgy_arr) throws SQLException, Exception {
    System.out.println(" B.DAO : updateBook() 호출");

    int updateCount = 0;

    /* 1. 도서 정보 UPDATE */
    if(updateBookDetails(book) <= 0) return updateCount;

    /* 2. 해당 도서의 B_ID에 따른 서브 분류 데이터 SELECT */
    List<Integer> existingSubcategoriesList = getExistingSubcategories(book.getB_id());
    int existingSubcategoriesDB = existingSubcategoriesList.size();
    int updatedSubcategories = sub_catgy_arr.length;
    System.out.println("updatedSubcategories : "+updatedSubcategories);

    /* 위의 UPDATE가 잘 된 경우 실행 */
    if (existingSubcategoriesDB > 0 && updatedSubcategories > 0) {
        // 기존의 서브 분류(DB) 데이터 및 변경할 서브 분류 데이터가 있는 경우 
        System.out.println("기존의 서브 분류(DB) 데이터 및 변경할 서브 분류 데이터가 있습니다.");
        int result = updatedSubcategories-existingSubcategoriesDB;
        int DMLcase = 0;
        if(result > 0) {
            DMLcase = 1;
        } else if (result < 0) {
            DMLcase = -1;
        } 

        /* 기존의 서브 분류(DB) 기준으로 DML 처리 */
        switch (DMLcase) {
        case 1:
            /* 변경할 서브 분류 데이터가 더 많은 경우 
             * 1. 기존의 서브 분류(DB) 개수만큼 수정 
             * 2. (변경할 서브 분류 데이터 길이 - 기존의 서브 분류(DB) 개수)만큼 INSERT */ 
            System.out.println("변경할 서브 분류 데이터가 더 많은 경우");
            updateCount = updateSubCatgys(existingSubcategoriesList, 
            				    existingSubcategoriesDB, 
                                            sub_catgy_arr, 
                                            book.getB_id());
            updateCount += insertSubCatgys(existingSubcategoriesDB, 
            				    updatedSubcategories, 
                                            sub_catgy_arr, 
                                            book.getB_id());
            break;

        case -1:
            /* 기존의 서브 분류(DB) 데이터가 더 많은 경우
             * 1. 변경할 서브 분류 데이터 길이만큼 수정
             * 2. 남은 기존 서브 분류(DB) 데이터 DELETE */
            System.out.println("기존의 서브 분류(DB) 데이터가 더 많은 경우");
            updateCount = updateSubCatgys(existingSubcategoriesList, 
            				    updatedSubcategories, 
                                            sub_catgy_arr, 
                                            book.getB_id());
            updateCount += deleteSubCatgys(existingSubcategoriesList,
            				    updatedSubcategories, 
                                            existingSubcategoriesDB, 
                                            book.getB_id());
            break;

        default:
            /* 데이터 개수가 동일한 경우 */
            System.out.println("데이터 개수가 동일한 경우");
            updateCount = updateSubCatgys(existingSubcategoriesList, 
            				    updatedSubcategories, 
                                            sub_catgy_arr, 
                                            book.getB_id());
        }
    }

    else if(existingSubcategoriesDB > 0 && updatedSubcategories <= 0 ) {
        // 기존의 서브 분류(DB) 데이터가 있지만 변경할 서브 분류 데이터가 없는 경우
        System.out.println("기존의 서브 분류(DB) 데이터가 있지만 변경할 서브 분류 데이터가 없습니다.");
        updateCount = deleteSubCatgys(existingSubcategoriesList, 
        					0, 
                                        existingSubcategoriesDB, 
                                        book.getB_id());
    }

    else if(existingSubcategoriesDB == 0 && updatedSubcategories > 0) {
        // 기존의 서브 분류(DB) 데이터는 없지만 변경할 서브 분류 데이터는 있는 경우
        System.out.println("기존의 서브 분류(DB) 데이터는 없지만 변경할 서브 분류 데이터가 있습니다.");
        updateCount = insertSubCatgys(0, 
        				updatedSubcategories, 
                                        sub_catgy_arr, 
                                        book.getB_id());
    }

    else {
        System.out.println("기존의 서브 분류(DB) 및 변경할 서브 분류 데이터가 없습니다.");
        updateCount = 1;
    }

    System.out.println(" B.DAO : updateBook() 종료");
    return updateCount;
}

/** 도서 정보 UPDATE하는 DML 트랜잭션 메서드 */
public int updateBookDetails(Book book) throws SQLException {
    int updateCount = 0;
    String update_sql = "UPDATE jspbookshop.book "
                    + "SET b_name=?, b_writer=?, b_translator=?, b_publisher=?, b_bc_code=?, "
                    + "b_price=?, b_image=?, b_page=?, b_publish_date=?, b_content=? "
                    + "where b_id=?";

    try(PreparedStatement pstmt = conn.prepareStatement(update_sql)) {
        pstmt.setString(1, book.getB_name());
        pstmt.setString(2, book.getB_writer());
        pstmt.setString(3, book.getB_translator());
        pstmt.setString(4, book.getB_publisher());
        pstmt.setInt(5, book.getB_bc_code());
        pstmt.setInt(6, book.getB_price());
        pstmt.setString(7, book.getB_image());
        pstmt.setInt(8, book.getB_page());
        pstmt.setString(9, book.getB_publish_date());
        pstmt.setString(10, book.getB_content());
        pstmt.setInt(11, book.getB_id());

        updateCount = pstmt.executeUpdate();
    } catch (SQLException e) {
        System.out.println(" updateBookDetails ERROR1 pstmt 처리 도중 에러 : "+e);
    } 

    return updateCount;

}

/** 도서 서브 분류 데이터를 SELECT하는 DQL 트랜잭션 메서드 */
public List<Integer> getExistingSubcategories(int b_id) throws SQLException {
    List<Integer> existingSubcategoriesList = new ArrayList<>();
    String select_sql = "SELECT bsc_id FROM jspbookshop.booksubcatgy WHERE bsc_b_id = ?";

    try (PreparedStatement subSelectPstmt = conn.prepareStatement(select_sql)) {
        subSelectPstmt.setInt(1, b_id);

        try(ResultSet rs = subSelectPstmt.executeQuery()) {
            while (rs.next()) {
                existingSubcategoriesList.add(rs.getInt(1));
            } 
        } catch (SQLException e) {
            System.out.println(" getExistingSubcategories ERROR2 rs 처리 도중 에러 : "+e);
        }	
    } catch (SQLException e) {
        System.out.println(" getExistingSubcategories ERROR1 subSelectPstmt 처리 도중 에러 : "+e);
    }

    System.out.println("existingSubcategoriesDB : "+existingSubcategoriesList.size());
    return existingSubcategoriesList;
}

/** 도서 서브 분류 데이터를 INSERT하는 DML 트랜잭션 메서드 */
public int insertSubCatgys(int startCount, 
			    int limitCount, 
                            int mod_arr[], 
                            int b_id) throws SQLException {
    int updateCount = 0;
    String insert_sql = "INSERT INTO jspbookshop.booksubcatgy(bsc_b_id, bsc_bc_code) VALUES(?, ?)";

    try(PreparedStatement subInsertPstmt = conn.prepareStatement(insert_sql)) {
        subInsertPstmt.setInt(1, b_id);
        for(int i=startCount; i<limitCount; i++) {
            subInsertPstmt.setInt(2, mod_arr[i]);
            updateCount += subInsertPstmt.executeUpdate();
        }
    } catch (SQLException e) {
        System.out.println(" insertSubCatgys ERROR1 subInsertPstmt 처리 도중 에러 : "+e);
    }

    return updateCount;
}

/** 도서 서브 분류 데이터를 UPDATE하는 DML 트랜잭션 메서드 */
public int updateSubCatgys(List<Integer> catgyIdList, 
			    int limitCount, 
                            int mod_arr[], 
                            int b_id) throws SQLException {
    int updateCount = 0;
    String update_sql = "UPDATE jspbookshop.booksubcatgy "
            + "SET bsc_bc_code = ? "
            + "WHERE bsc_b_id = ? "
            + "AND bsc_id = ?";

    try(PreparedStatement subUpdatePstmt = conn.prepareStatement(update_sql)) {
        for(int i=0; i<limitCount; i++) {
            subUpdatePstmt.setInt(1, mod_arr[i]);
            subUpdatePstmt.setInt(2, b_id);
            subUpdatePstmt.setInt(3, catgyIdList.get(i));
            updateCount += subUpdatePstmt.executeUpdate();
        }
    } catch (SQLException e) {
        System.out.println(" updateSubCatgys ERROR1 subUpdatePstmt 처리 도중 에러 : "+e);
    }

    return updateCount;
}

/** 도서 서브 분류 데이터를 DELETE하는 DML 트랜잭션 메서드 */
public int deleteSubCatgys(List<Integer> catgyIdList, 
			    int startCount, 
                            int limitCount, 
                            int b_id) throws SQLException {
    int updateCount = 0;
    String delete_sql = "DELETE FROM jspbookshop.booksubcatgy "
                    + "WHERE bsc_id = ? "
                    + "AND bsc_b_id = ?";

    try(PreparedStatement subDeletePstmt = conn.prepareStatement(delete_sql)) {
        for(int i=startCount; i<limitCount; i++) {
            subDeletePstmt.setInt(1, catgyIdList.get(i));
            subDeletePstmt.setInt(2, b_id);
            updateCount += subDeletePstmt.executeUpdate();
        }
    } catch (SQLException e) {
        System.out.println(" deleteSubCatgys ERROR1 subDeletePstmt 처리 도중 에러 : "+e);
    }

    return updateCount;
}

 

 

클라 딴 실행 화면

도서가 수정되고, 수정된 정보가 클라 딴에서 잘 출력되는지 확인해 보자.

 

0. 예시 도서 데이터 확인

예시가 될 도서 아이디는 123, DB에서 해당 도서의 정보를 먼저 확인해 보자.

아래 쿼리문은 특정 도서 ID (b_id)가 123인 도서에 대한 정보를 반환한다.

해당 도서에 대한 서브 분류(booksubcatgy 테이블) 데이터가 없을 수 있으므로 book 테이블을 기준으로 하는 LEFT JOIN을 사용해 3개의 테이블을 조인해 정보를 반환하도록 한다.

 

SELECT 
	b.b_id, b.b_name, b.b_writer, b.b_translator, b.b_publisher, b.b_bc_code "메인분류 코드", 
	b.b_price, b.b_image, b.b_page, b.b_content,
    bsc.bsc_bc_code "서브 분류 코드",
    bc.bc_name "서브 분류 코드명", bc.bc_code_ref_md "서브 middle 코드", bc.bc_code_ref_mn "서브 main 코드"
FROM book b
LEFT JOIN booksubcatgy bsc ON b.b_id = bsc.bsc_b_id
LEFT JOIN bookcatgycode bc ON bsc.bsc_bc_code = bc.bc_code
WHERE b.b_id = 123;

 

▼ DB 조회 결과


조회 결과, b_id가 123인 도서의 도서명은 "도파민네이션", 메인분류코드는 "100301", 가격은 "14400", 서브분류는 존재하지 않는다는 것을 알 수 있다.

 

b_id 123 도서 정보 조회


 

1. 도서 수정 페이지에서 수정(bookView.jsp)

기존 도서 정보에서 정가와 메인 분류 값을 아래 이미지 속 값으로 수정하고, 기존에 없던 서브 분류를 새로 2개 추가해 주었다.

수정한 후, "수정" 버튼을 클릭한다.

 

수정 페이지에서 도서 수정

 

2. 수정 후 리다이렉트된 페이지(bookView.jsp)

아래 이미지는 도서 정보를 수정한 후 리다이렉트 된 페이지이다. 

Action 클래스에서 호출할 페이지를 bookView.jsp로 설정해 두었기 때문에 똑같은 JSP 페이지가 호출되었지만 이전 bookView.jsp 페이지와는 전혀 다른 새로운 페이지이다.

 

리다이렉트된 페이지

 

▼ DB 조회 결과


해당 도서 정보가 DB에 잘 수정, 반영되었는지 확인해 보자.

메인분류코드는 "100107", 가격은 "15000",  서브분류는 2개가 추가된 것을 알 수 있다.

 

UPDATE 후 b_id 123 도서 정보 조회


 

 


 

 

이렇게 모든 서버 딴 로직과 클라 딴 구현이 완성되었다.

이로써 다중 카테고리 구현이 마무리되었다.

정말 길고 힘든 과정이었지만 원하는 방향으로 구현되어 기쁘게 생각한다.

 

 

 

도서 사이트에서 다중 카테고리 구현하기 끝.