웹개발 수업/JDBC
[Day +47 / JBDC]PreparedStatement 도서 관리 프로그램 만들기
Chole Woo
2021. 8. 27. 22:02
210827 금
PreparedStatement
: Connection객체의 preparedStatement() 메소드를 사용하여 객체 생성
-SQL문장이 미리 컴파일 되고 실행 시간동안 인수 값을 위한 공간을 확보한다는 점에서 Statement와 다름
-각각의 인수에 대해 위치홀더(?)를 사용하여 SQL문장을 정의할 수 있게 함
try{ String query = “INSERT INTO MEMBER VALUES(?, ?)”;
pstmt = conn.preparedStatement(query); pstmt.setString(1, id);
pstmt.setString(2, password); } catch(SQLException e){ e.printStackTrace(); }
*장점
1) 미리 설정을 해놔서 자리를 만들어 놓기 때문에 실제 실행시 빠르다
2) 간결하다
3) sql injection도 피할 수 있다
1> Controller
package book.controller;
import java.util.List;
import book.model.service.BookService;
import book.model.vo.Book;
import book.view.BookView;
/*
* Controller : 1. 요청 받은 데이터를 검증하고 서비스 클래스의 메소드에 인자로 정보를 전달
* 2. 수행 결과를 해당하는 뷰 페이지에 연결하는 역할을 수행
* */
public class BookController {
BookService bs = new BookService();
BookView bv = new BookView();
// 1. 도서 신규 등록용 메소드
public void insertBook(Book b) {
int result = bs.insertBook(b);
if(result > 0) {
bv.displayResult("insert");
} else {
bv.displayError("insert");
}
}
// 6. 도서 전체 목록 조회용 메소드
public void selectBookList() {
List<Book> bookList = bs.selectBookList();
bv.displayBookList(bookList);
}
// 2. 도서 수정용 메소드
public void updateBook(int bookId, Book b) {
// 수정할 내용이 담긴 book 객체에 bookId 함께 담아서 Service로 넘김
b.setBookId(bookId);
int result = bs.updateBook(b);
if(result > 0) {
bv.displayResult("update");
} else {
bv.displayError("update");
}
}
// 3. 도서 삭제용 메소드
public void deleteBook(int bookId) {
int result = bs.deleteBook(bookId);
if(result > 0) {
bv.displayResult("delete");
} else {
bv.displayError("delete");
}
}
// 4. 도서 번호로 도서 조회용 메소드
public void searchBookId(int bookId) {
Book b = bs.searchBookId(bookId);
bv.displayBook(b);
}
// 5. 도서명 검색
public void searchBookTitle(String bookTitle) {
// bookTitle 키워드 포함 여부로 해당하는 검색 된 도서 리스트 리턴
List<Book> bookList = bs.searchBookTitle(bookTitle);
bv.displayBookList(bookList);
}
}
2> BookDao
package book.model.dao;
import static common.JDBCTemplate.close;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import book.model.vo.Book;
/* Dao (Data Access Object) : 실제 Data에 접근해서 동작할 코드 작성
* C : create (insert)
* R : read (select)
* U : update
* D : delete
* */
public class BookDao {
// 1. 도서 1권 insert
public int insertBook(Connection conn, Book b) {
PreparedStatement pstmt = null;
int result = 0;
String sql = "insert into book "
+ "values (seq.nextval, ?, ?, ?, to_date(?, 'rrrr/mm/dd'), ?)";
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, b.getTitle());
pstmt.setString(2, b.getAuthor());
pstmt.setString(3, b.getPublisher());
pstmt.setString(4, b.getPublisherDate());
pstmt.setInt(5, b.getPrice());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
// 6. 도서 전체 목록 조회
public List<Book> selectBookList(Connection conn) {
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = "select * from book";
List<Book> bookList = new ArrayList<>();
try {
pstmt = conn.prepareStatement(sql);
rset = pstmt.executeQuery();
while(rset.next()) {
Book b = new Book(rset.getInt("book_id"),
rset.getString("title"),
rset.getString("author"),
rset.getString("publisher"),
rset.getString("publisher_date"),
rset.getInt("price"));
bookList.add(b);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return bookList;
}
// 2. 도서 정보 수정
public int updateBook(Connection conn, Book b) {
PreparedStatement pstmt = null;
int result = 0;
String sql = "update book set title = ?, author = ?, publisher = ?, "
+ "publisher_date = to_date(?, 'rrrr/mm/dd'), price = ? "
+ "where book_id = ?";
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, b.getTitle());
pstmt.setString(2, b.getAuthor());
pstmt.setString(3, b.getPublisher());
pstmt.setString(4, b.getPublisherDate());
pstmt.setInt(5, b.getPrice());
pstmt.setInt(6, b.getBookId());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
// 3. 도서 삭제용 메소드
public int deleteBook(Connection conn, int bookId) {
PreparedStatement pstmt = null;
int result = 0;
String sql = "delete from book where book_id = ?";
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, bookId);
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
// 4. 도서 번호로 도서 조회용 메소드
public Book searchBookId(Connection conn, int bookId) {
String sql = "select * from book where book_id = ?";
PreparedStatement pstmt = null;
ResultSet rset = null;
Book b = null;
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, bookId);
rset = pstmt.executeQuery();
if (rset.next()) {
b = new Book(rset.getInt("book_id"),
rset.getString("title"),
rset.getString("author"),
rset.getString("publisher"),
rset.getString("publisher_date"),
rset.getInt("price"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return b;
}
// 5. 도서명 조회
public List<Book> searchBookTitle(Connection conn, String bookTitle) {
PreparedStatement pstmt = null;
ResultSet rset = null;
List<Book> bookList = new ArrayList<>();
// like 연산자 사용 시 ? 위치홀더 인식을 위해 '%'와 분리하여 작성하여야 함
String sql = "select * from book where title like '%' || ? || '%'";
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, bookTitle);
rset = pstmt.executeQuery();
while(rset.next()) {
Book b = new Book(rset.getInt(1),
rset.getString(2),
rset.getString(3),
rset.getString(4),
rset.getString(5),
rset.getInt(6));
bookList.add(b);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return bookList;
}
}
3> BookService
package book.model.service;
import static common.JDBCTemplate.*;
import java.sql.Connection;
import java.util.List;
import book.model.dao.BookDao;
import book.model.vo.Book;
/* Service : 1. 컨트롤러로부터 매개변수로 정보를 전달 받는다
* 2. Connection 객체를 생성한다
* 3. 생성한 Connection 객체와 전달 받은 매개변수를 DAO 클래스의 메소드로 전달한다
* 4. 수행 결과에 따른 트랜잭션을 관리한다
* */
public class BookService {
BookDao bd = new BookDao();
// 1. 도서 신규 등록용 메소드
public int insertBook(Book b) {
Connection conn = getConnection();
int result = bd.insertBook(conn, b);
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
close(conn);
return result;
}
// 6. 도서 전체 목록 조회용 메소드
public List<Book> selectBookList() {
Connection conn = getConnection();
List<Book> bookList = bd.selectBookList(conn);
close(conn);
return bookList;
}
// 2. 도서 정보 수정용 메소드
public int updateBook(Book b) {
Connection conn = getConnection();
int result = bd.updateBook(conn, b);
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
close(conn);
return result;
}
// 3. 도서 삭제용 메소드
public int deleteBook(int bookId) {
Connection conn = getConnection();
int result = bd.deleteBook(conn, bookId);
if(result > 0){
commit(conn);
}else{
rollback(conn);
}
close(conn);
return result;
}
// 4. 도서 번호로 도서 조회용 메소드
public Book searchBookId(int bookId) {
Connection conn = getConnection();
Book b = bd.searchBookId(conn, bookId);
close(conn);
return b;
}
// 5. 도서명 검색
public List<Book> searchBookTitle(String bookTitle) {
Connection conn = getConnection();
List<Book> bookList = bd.searchBookTitle(conn, bookTitle);
close(conn);
return bookList;
}
}
4> Book
package book.model.vo;
public class Book {
private int bookId;
private String title;
private String author;
private String publisher;
private String publisherDate;
private int price;
/*
BOOK_ID NUMBER
TITLE VARCHAR2(50 BYTE)
AUTHOR VARCHAR2(20 BYTE)
PUBLISHER VARCHAR2(20 BYTE)
PUBLISHER_DATE DATE
PRICE NUMBER
*/
public Book() {}
// 매개변수 생성자 (bookId 제외 - DB의 Sequence 객체로 발생 시킬 예정)
public Book(String title, String author, String publisher, String publisherDate, int price) {
super();
this.title = title;
this.author = author;
this.publisher = publisher;
this.publisherDate = publisherDate;
this.price = price;
}
// 매개변수 생성자 (모든 필드)
public Book(int bookId, String title, String author, String publisher, String publisherDate, int price) {
super();
this.bookId = bookId;
this.title = title;
this.author = author;
this.publisher = publisher;
this.publisherDate = publisherDate;
this.price = price;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public String getPublisherDate() {
return publisherDate;
}
public void setPublisherDate(String publisherDate) {
this.publisherDate = publisherDate;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book [bookId=" + bookId + ", title=" + title + ", author=" + author + ", publisher=" + publisher
+ ", publisherDate=" + publisherDate + ", price=" + price + "]";
}
}
5-1> BookMenu
package book.view;
import java.util.Scanner;
import book.controller.BookController;
import book.model.vo.Book;
public class BookMenu {
Scanner sc = new Scanner(System.in);
BookController bc = new BookController();
public void displayMenu() {
while(true) {
System.out.println("*** 도서 관리 프로그램 ***");
System.out.println("1. 도서 추가");
System.out.println("2. 도서 정보 수정");
System.out.println("3. 도서 삭제");
System.out.println("4. 도서 아이디로 조회");
System.out.println("5. 도서 제목으로 조회");
System.out.println("6. 도서 목록 전체 조회");
System.out.println("9. 끝내기");
System.out.print("번호 선택 : ");
int no = Integer.parseInt(sc.nextLine());
switch(no) {
case 1 :
bc.insertBook(inputBook());
break;
case 2 :
bc.updateBook(inputBookId(), inputBook());
break;
case 3 :
bc.deleteBook(inputBookId());
break;
case 4 :
// 한 권의 책 조회이므로 메소드 리턴 타입은 Book으로 처리
bc.searchBookId(inputBookId());
break;
case 5 :
bc.searchBookTitle(inputBookTitle());
break;
case 6 :
bc.selectBookList();
break;
case 9 :
System.out.println("프로그램을 종료합니다.");
return;
default :
System.out.println("번호를 잘못 누르셨습니다.");
}
}
}
// 도서 정보 입력용 메소드
public Book inputBook() {
System.out.print("도서명 : ");
String title = sc.nextLine();
System.out.print("저자명 : ");
String author = sc.nextLine();
System.out.print("출판사 : ");
String publisher = sc.nextLine();
System.out.print("출판일(19990101 형식으로 입력) : ");
String publisherDate = sc.nextLine();
System.out.print("가격 : ");
int price = Integer.parseInt(sc.nextLine());
return new Book(title, author, publisher, publisherDate, price);
}
// 도서 아이디 입력용 메소드
public int inputBookId() {
System.out.print("도서 번호 입력 : ");
return Integer.parseInt(sc.nextLine());
}
// 도서 제목 입력용 메소드
public String inputBookTitle() {
System.out.print("검색할 도서명 입력 :");
return sc.nextLine();
}
}
5-2> BookView
package book.view;
import java.util.List;
import book.model.vo.Book;
public class BookView {
// 결과 출력용 메소드
public void displayResult(String msg) {
switch(msg) {
case "insert" :
System.out.println("도서 정보 입력이 완료 되었습니다.");
break;
case "update" :
System.out.println("도서 정보 수정이 완료 되었습니다.");
break;
case "delete" :
System.out.println("도서 정보 삭제가 완료 되었습니다.");
break;
}
}
// 에러 출력용 메소드
public void displayError(String msg) {
switch(msg) {
case "insert" :
System.out.println("도서 정보 입력에 실패하였습니다.");
break;
case "update" :
System.out.println("도서 정보 수정에 실패하였습니다.");
break;
case "delete" :
System.out.println("도서 정보 삭제에 실패하였습니다.");
break;
}
}
// 도서 목록 출력용 메소드
public void displayBookList(List<Book> bookList) {
if(bookList.isEmpty()) {
System.out.println("조회 결과가 없습니다.");
return;
}
System.out.println("*** 도서 목록 출력 ***");
for(Book b : bookList) {
System.out.println(b);
}
}
// 도서 출력용 메소드
public void displayBook(Book b) {
if(b == null) {
System.out.println("조회 결과가 없습니다.");
return;
}
System.out.println("*** 도서 출력 ***");
System.out.println(b);
}
}
6> JDBCTemplate
package common;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
// JDBC 업무 처리에 있어 공통 된 부분을 static 메소드로 정의하여
// 공유하여 사용할 수 있도록 함
public class JDBCTemplate {
// 1. Connection 객체 생성 메소드
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe",
"student", "student");
// 자동 커밋 처리 되지 않도록 설정
// (어플리케이션 내에서 판별하여 commit/rollback 수행하기 위해)
conn.setAutoCommit(false);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
// 2. 트랜잭션 관리 메소드 (commit/rollback)
public static void commit(Connection conn) {
try {
if(conn != null && !conn.isClosed()) {
conn.commit();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollback(Connection conn) {
try {
if(conn != null && !conn.isClosed()) {
conn.rollback();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 3. 사용한 객체 close 메소드
public static void close(Connection conn) {
try {
if(conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// PreparedStatement는 Statement의 후손이므로 부모 타입인 Statement만
// 작성하면 같이 사용 가능 (다형성)
public static void close(Statement stmt) {
try {
if(stmt != null && !stmt.isClosed()) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rset) {
try {
if(rset != null && !rset.isClosed()) {
rset.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
7> Run
package book.run;
import book.view.BookMenu;
public class Run {
public static void main(String[] args) {
BookMenu bm = new BookMenu();
bm.displayMenu();
}
}