일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 자바
- 오라클
- SESSION
- Spring
- Spring MVC
- jquery
- 일단_해보는거야
- 로그인과 장바구니 구현
- 프로그래머스
- MVC
- jsp 프로젝트
- java
- Level 1
- 다중 카테고리 구현
- 이메일로 인증코드 전송 구현
- 교보문고 따라하기
- js
- json
- 코딩
- 고객센터 구현
- jakarta.mail
- Sts
- 대분류/중분류/소분류
- 세션
- ajax
- Oracle
- 스프링
- 인증코드로 비밀번호 변경 구현
- jsp
- MySQL
감 잃지말고 개발하기
[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #8. 데이터 수정하기 (3) (마지막) 본문
지난 포스팅 "도서 사이트에서 다중 카테고리 구현하기 #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", 서브분류는 존재하지 않는다는 것을 알 수 있다.
1. 도서 수정 페이지에서 수정(bookView.jsp)
기존 도서 정보에서 정가와 메인 분류 값을 아래 이미지 속 값으로 수정하고, 기존에 없던 서브 분류를 새로 2개 추가해 주었다.
수정한 후, "수정" 버튼을 클릭한다.
2. 수정 후 리다이렉트된 페이지(bookView.jsp)
아래 이미지는 도서 정보를 수정한 후 리다이렉트 된 페이지이다.
Action 클래스에서 호출할 페이지를 bookView.jsp로 설정해 두었기 때문에 똑같은 JSP 페이지가 호출되었지만 이전 bookView.jsp 페이지와는 전혀 다른 새로운 페이지이다.
▼ DB 조회 결과
해당 도서 정보가 DB에 잘 수정, 반영되었는지 확인해 보자.
메인분류코드는 "100107", 가격은 "15000", 서브분류는 2개가 추가된 것을 알 수 있다.
이렇게 모든 서버 딴 로직과 클라 딴 구현이 완성되었다.
이로써 다중 카테고리 구현이 마무리되었다.
정말 길고 힘든 과정이었지만 원하는 방향으로 구현되어 기쁘게 생각한다.
도서 사이트에서 다중 카테고리 구현하기 끝.
'JSP > MVC' 카테고리의 다른 글
[JSP][MVC][MySQL] 도서 목록 페이지에서 필터링 구현하기 #2. 클라 딴 (1) (0) | 2023.08.03 |
---|---|
[JSP][MVC][MySQL] 도서 목록 페이지에서 필터링 구현하기 #1. 구현 틀 잡기 (0) | 2023.08.02 |
[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #7. 데이터 수정하기 (2) (0) | 2023.06.29 |
[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #6. 데이터 수정하기 (1) (0) | 2023.06.26 |
[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #5. 데이터 출력하기 (2) (0) | 2023.06.13 |
[JSP][MVC][MySQL] 도서 사이트에서 다중 카테고리 구현하기 #4. 데이터 출력하기 (1) (0) | 2023.06.12 |