CRUD : Create Read Update Delete
[JSP15_JDBC] JDBC 연결 + 게시판 작성
1. 게시판 설계
1) 게시판 사이클
: 글 생성 - 글 목록보기 - 글 내용읽기 - 글 수정 - 글 삭제
2) 글 작성시 작성자와 글제목은 필수사항
3) 조회수 구현
2. Dynamic Web Project 생성 후 DB Connect
: 데이터베이스 > 새 데이터베이스 연결
> Oracl, Project는 JSP15_JDBC > Next 클릭
> Host, Datebase, Username, Password 기입 후 Test Connection해보고 정상적으로 연결되면 Finish 클릭
3. ER Diagram 사용하여 실습용 데이터베이스와 테이블 생성
** test_write_orcle.erm
** test_write_orcle.sql
/* Drop Tables */
DROP TABLE test_write CASCADE CONSTRAINTS;
/* Create Tables */
CREATE TABLE test_write
(
wr_uid number NOT NULL,
wr_subject varchar2(200) NOT NULL,
wr_content clob,
wr_name varchar2(40) NOT NULL,
wr_viewcnt number DEFAULT 0,
wr_regdate date DEFAULT SYSDATE,
PRIMARY KEY (wr_uid)
);
-- 시퀀스
CREATE SEQUENCE TEST_WRITE_SEQ;
-- 기본데이터 작성
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '첫째글:방가요', '안녕하세요', '김희철', 0, '2017-03-02');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '둘째글:헤헤헤','1111', '김수길', 0, '2017-03-02');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '세째글:힘내세요', '7394', '최진덕' , 0, '2017-08-12');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '네째글: ... ', '9090', '이혜원', 0, '2018-02-09');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '다섯째글: 게시판', '7531', '박수찬', 0, sysdate);
-- 저장되어 있는 데이터 확인
SELECT * FROM test_write;
4. 해당 DB의 JDBC 라이브러리 설치(Dynamic Web Project)
: WebContent > Web-INF > lib 폴더에 붙여 넣기(반드시 해당 작업 필요!)
5. JDBC 연동 순서
1) JDBC 드라이버 로딩
: Class.forName("oracle.jdbc.driver.OracleDriver");
2) 데이터베이스 연결
: DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "scott0316", "tiger0316");
3) Statement 객체나 PreparedStatement 객체 생성
4) SQL 쿼리 실행
: executeQuery() ResulteSet 리턴, executeUpdate() int 리턴
5) ResulteSet 리턴한 경우, ResultSet 객체로부터 데이터 추출
: row 관련 메소드 - next(), previous(), first(), last(), ...
Column값 추출 메소드 - getString(), getInt(), ...
6) 데이터베이스 자원 반납
: close()
** Connection은 페이지당(request 당) 한 번만 생성하면 충분함
** 하나의 Connection으로부터 복수 개의 Statement, PreparedStatement 생성하여 운용 가능
** 자원 반납 절대 잊지 말자!!
6. 트랜잭션 (transaction)
: 데이터 조작 동작의 단위,
하나의 데이터 조작 동작을 하기 위해 한개 뿐만 아니라, 여러 개의 쿼리가 수행될 수도 있다.
7. 필요한 jsp 만들기
: db_test, write, writeOk, list, view, update, updateOk, deleteOk
8. DB 커넥션 테스트, JDBC 기본 코드 작성(앞으로 트랜젝션이 발생하는 모든 페이지의 기본 베이스가 될 예정)
** db_test.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%-- JDBC 관련 import --%>
<%@ page import = "java.sql.*" %>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
// * 여기에 쿼리문을 작성해준다.
%>
<%
try {
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜젝션 실행
// * 여기에 트랜젝션 실행문을 기재한다.
} catch(Exception e) {
e.printStackTrace();
// * 여기서 예외 처리를 하든지, 예외 페이지를 설정해줘야 한다.
} finally {
// 리소스 해제
try {
if(rs != null) {rs.close();}
if(stmt != null) {stmt.close();}
if(pstmt != null) {pstmt.close();}
if(conn != null) {conn.close();}
} catch(Exception e) {
e.printStackTrace();
}
}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
** 정상적으로 DB 커넥션 완료...!!
9. 폼 유효성 체크 (form validation) 필수!!
: 서버 측에 parameter를 넘기기 전에, 반드시 데이터의 유효성 검사 (validation)을 해야 한다.
10. 글 생성
: name(작성자)와 subject(글제목)는 필수 입력 사항, submit 하기 전에 입력여부 반드시 확인 필요하며
[잊지말자!] 프론트엔드와 백엔드 모두 유효성 검사는 필수다!!!!
** write.jsp : form 작성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>글작성</title>
</head>
<script>
function chkSubmit() { // 폼 검증
frm = document.forms["frm"];
var name = frm["name"].value.trim();
var subject = frm["subject"].value.trim();
// 프론트 엔드에서 폼 유효성 체크, 검증하기
if(name == "") {
alert('작성자 란은 반드시 입력해야 합니다.');
frm["name"].focus();
return false;
}
if(subject == "") {
alert('제목은 반드시 작성해야 합니다.');
frm["subject"].focus();
return false;
}
return true;
}
</script>
<body>
<h2>글작성</h2>
<%-- 글 내용이 많을수 있기 때문에 POST 방식 사용 --%>
<form name="frm" action="writeOk.jsp" method="post" onsubmit="return chkSubmit()">
작성자:
<input type="text" name="name"/><br>
제목:
<input type="text" name="subject"/><br>
내용:<br>
<textarea name="content"></textarea>
<br><br>
<input type="submit" value="등록"/>
</form>
<br>
<button type="button" onclick="location.href='list.jsp'">목록으로</button>
</body>
</html>
** writeOk.jsp : DB 등록
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%-- JDBC 관련 import --%>
<%@ page import = "java.sql.*"%>
<%
// post 방식은 한글 인코딩이 반드시 필요!(누락되지 않도록 조심하기!!)
request.setCharacterEncoding("utf-8");
// 입력한 값 받아오기
String name = request.getParameter("name");
String subject = request.getParameter("subject");
String content = request.getParameter("content");
// 백엔드에서 폼 유효성 체크, 검증하기
// name, subject 는 비어 있으면 안된다(not null)
// null 이거나 빈 문자열이면 이전 화면으로 돌아가기
if(name == null || subject == null ||
name.trim().equals("") || subject.trim().equals("")) {
%>
<script>
alert('작성자 이름, 글 제목은 필수입니다!');
history.back(); // history.go(-1)
</script>
<%
return; // ★ [매우 매우 중요!] 더이상 JSP 프로세싱 하지 않도록 종료 ★
}
%>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
String sql_insert = "INSERT INTO test_write "
+ "(wr_uid, wr_subject, wr_content, wr_name) "
+ "VALUES(test_write_seq.nextval, ?, ?, ?)"
;
%>
<%
try {
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜젝션 실행
pstmt = conn.prepareStatement(sql_insert);
pstmt.setString(1, subject);
pstmt.setString(2, content);
pstmt.setString(3, name);
cnt = pstmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace(); // 에러를 콘솔창에서 보고 싶을때
//throw e; // 에러를 직접 웹 페이지에서 보고 싶을때
// * 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
} finally {
// 리소스 해제
try {
if(rs != null) {rs.close();}
if(stmt != null) {stmt.close();}
if(pstmt != null) {pstmt.close();}
if(conn != null) {conn.close();}
} catch(Exception e) {
e.printStackTrace();
}
}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<% if(cnt == 0) { %>
<script>
alert('등록 실패!!');
history.back(); // 브라우저가 직전에 직전 페이지(입력중 페이지로)
</script>
<% } else {%>
<script>
alert('등록 성공, 리스트를 출력합니다!');
location.href = "list.jsp";
</script>
<% } %>
11. 서버쪽 검증이 제대로 먹혔는지 확인하는 방법
: postman 이용하기
12. 글 목록보기
: 글 생성 후 목록 보기를 만드는 이유는 jsp별 기능을 만들고
정상적으로 반영됬는지 확인하기 위해서 번거롭게 DB에 직접 검색을 해야함,
이러한 번거로움을 덜기 위해 리스트를 먼저 만듦.
** list.sql
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
final String SQL_WRITE_SELECT
= "SELECT * FROM test_write ORDER BY wr_uid DESC";
%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>글 목록</title>
<style>
table {width: 100%;}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
</head>
<body>
<%
try {
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜젝션 실행
pstmt = conn.prepareStatement(SQL_WRITE_SELECT);
rs = pstmt.executeQuery();
out.println("쿼리 성공<br>");
%>
<hr>
<h2>리스트</h2>
<table>
<tr>
<th>UID</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>등록일</th>
</tr>
<%
while (rs.next()) {
out.println("<tr>");
int uid = rs.getInt("wr_uid");
String subject = rs.getString("wr_subject");
String name = rs.getString("wr_name");
int viewcnt = rs.getInt("wr_viewcnt");
Date d = rs.getDate("wr_regdate"); // java.sql
Time t = rs.getTime("wr_regdate"); // java.sql
String regdate = "";
if(d != null) {
regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " "
+ new SimpleDateFormat("hh:mm:ss").format(t);
}
out.println("<td>" + uid + "</td>");
out.println("<td><a href='view.jsp?uid=" + uid + "'>" + subject + "</a></td>");
out.println("<td>" + name + "</td>");
out.println("<td>" + viewcnt + "</td>");
out.println("<td>" + regdate + "</td>");
out.println("</tr>");
} // end while
%>
</table>
<br>
<button onclick="location.href='write.jsp'">신규등록</button>
<%
} catch(Exception e) {
e.printStackTrace();
// * 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
} finally {
// 리소스 해제
try {
if(rs != null) {rs.close();}
if(stmt != null) {stmt.close();}
if(pstmt != null) {pstmt.close();}
if(conn != null) {conn.close();}
} catch(Exception e) {
e.printStackTrace();
}
}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
</body>
</html>
13. 글내용 읽기
: View에서는 조회수 증가, 해당 글 내용 읽어오는 2가지 쿼리를 진행해야한다,
기본적으로 JDBC 프로그래밍은 Auto-Commit 되어 있기 때문에
여러 개의 쿼리로 트랜잭션 처리할 때는 Auto-Commit를 false 설정해야 한다.
Auto-Commit false 설정 후 모든 트렌젝션이 완료되었을때 commit() 처리,
하나라도 쿼리 실패시 rollback() 처리해야 한다.
** view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>
<%
// parameter 받아 오기
int uid = Integer.parseInt(request.getParameter("uid"));
// * 이 단계에서 parameter 검증 필요하나 생략.
%>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
// 여러 개의 쿼리가 하나의 트랜젝션인 경우 Auto-Commit을 하면 안된다.
// 둘 다 성공 후 commit 되어야 하며
// 하나라도 commit 되지 않으면 rollback 해야하는데
// Auto-Commit인 경우는 무조건 쿼리가 하나 끝나면 자동 저장되기 때문이다
final String SQL_WRITE_INC_VIEWCNT // 조회수 증가
= "UPDATE test_write SET wr_viewcnt = wr_viewcnt + 1 WHERE wr_uid = ?";
final String SQL_WRITE_SELECT_BY_UID // 글 읽어오기
= "SELECT * FROM test_write WHERE wr_uid = ?";
%>
<%
String name = "";
String subject = "";
String content = "";
String regdate = "";
int viewcnt = 0;
%>
<%
try{
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜잭션 실행
// ★[완전 중요!] 여기서 무조건 Auto-commit 비활성화해줘야함!!★
conn.setAutoCommit(false);
// 쿼리들 수행
pstmt = conn.prepareStatement(SQL_WRITE_INC_VIEWCNT);
pstmt.setInt(1, uid);
cnt = pstmt.executeUpdate();
pstmt.close();
pstmt = conn.prepareStatement(SQL_WRITE_SELECT_BY_UID);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
// 한개의 레코드만 select 된다.
if(rs.next()){
subject = rs.getString("wr_subject");
content = rs.getString("wr_content");
if(content == null) content = ""; // null 처리
name = rs.getString("wr_name");
viewcnt = rs.getInt("wr_viewcnt");
Date d = rs.getDate("wr_regdate");
Time t = rs.getTime("wr_regdate");
regdate = "";
if(d != null){
regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " "
+ new SimpleDateFormat("hh:mm:ss").format(t);
}
} else {
// wr_uid 값의 레코드가 없는 뜻
%>
<script>
alert("해당 정보가 삭제되거나 없습니다");
history.back();
</script>
<%
return; // 더이상 JSP 프로세싱 하지 않고 종료
} // end if
// ★[완전 중요!] 반드시 모든 쿼리 성공하면 commit 처리★
// Auto-commit 비활성화되어 있기 때문에
// commit을 안하면 진행했던 쿼리들이 저장이 안됨..!!
conn.commit();
} catch(Exception e){
e.printStackTrace();
// 예외 처리
// ★[완전 중요!] 반드시 모든 쿼리 성공하면 commit 처리해야함과 반대로
// 하나라도 쿼리 실패하면 rollback 해야한다..!!★
conn.rollback();
} finally {
// 리소스 해제
try {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(pstmt != null) pstmt.close();
if(conn != null) conn.close();
} catch(Exception e){
e.printStackTrace();
}
}
%>
<%-- 위 트랜잭션이 마무리 되면 페이지 보여주기 --%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>읽기 <%= subject %></title> <!-- title에 글제목 넣기 -->
</head>
<script>
// script 는 위치 구애를 받지 않는다
function chkDelete(uid) {
// 삭제 여부, 다시 확인하고 진행하기
var r = confirm('삭제하시겠습니까?');
if(r) {
location.href = 'deleteOk.jsp?uid=' + uid;
}
}
</script>
<body>
<body>
<h2>읽기 <%= subject %></h2>
<br>
UID : <%= uid %><br>
작성자 : <%= name %><br>
제목 : <%= subject %><br>
등록일 : <%= regdate %><br>
조회수 : <%= viewcnt %><br>
내용: <br>
<hr>
<div>
<%= content %>
</div>
<hr>
<br>
<button onclick="location.href='update.jsp?uid=<%= uid%>'">수정하기</button>
<button onclick="location.href = 'list.jsp'">목록보기</button>
<button onclick="chkDelete(<%= uid %>)">삭제하기</button>
<button onclick="location.href = 'write.jsp'">신규등록</button>
</body>
</html>
14. 글수정
: 작성자 그대로 제목과 내용만 수정 가능할 수 있도록 만듦.
** update.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>
<% // parameter 받아오기
int uid = Integer.parseInt(request.getParameter("uid"));
// * 이 단계에서 parameter 검증이 필요하나 생략
%>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
final String SQL_WRITE_SELECT_BY_UID // 글 읽어 오기
= "SELECT * FROM test_write WHERE wr_uid = ?";
%>
<%
String name = "";
String subject = "";
String content = "";
String regdate = "";
int viewcnt = 0;
%>
<%
try{
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜잭션 실행
pstmt = conn.prepareStatement(SQL_WRITE_SELECT_BY_UID);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
// 한개의 레코드만 select 된다.
if(rs.next()){
subject = rs.getString("wr_subject");
content = rs.getString("wr_content");
if(content == null) content = ""; // null 처리
name = rs.getString("wr_name");
viewcnt = rs.getInt("wr_viewcnt");
Date d = rs.getDate("wr_regdate");
Time t = rs.getTime("wr_regdate");
regdate = "";
if(d != null){
regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " "
+ new SimpleDateFormat("hh:mm:ss").format(t);
}
} else {
// wr_uid 값의 레코드가 없는 뜻
%>
<script>
alert("해당 정보가 삭제되거나 없습니다");
history.back();
</script>
<%
return; // 더이상 JSP 프로세싱 하지 않고 종료
} // end if
} catch(Exception e){
e.printStackTrace();
// 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
} finally {
// 리소스 해제
try {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(pstmt != null) pstmt.close();
if(conn != null) conn.close();
} catch(Exception e){
e.printStackTrace();
}
}
%>
<%-- 위 트랜잭션이 마무리 되면 페이지 보여주기 --%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>수정 <%= subject %></title>
</head>
<script>
function chkSubmit(){
frm = document.forms['frm'];
var subject = frm['subject'].value.trim();
if(subject == ""){
alert("제목은 반드시 작성해야 합니다");
frm['subject'].focus();
return false;
}
return true;
}
</script>
<body>
<h2>수정</h2>
<form name="frm" action="updateOk.jsp" method="post" onsubmit="return chkSubmit()">
<input type="hidden" name="uid" value="<%= uid %>"/>
작성자 : <%= name %><br> <%-- 작성자 이름 변경 불가 --%>
제목 :
<input type="text" name="subject" value="<%= subject %>"/><br>
내용: <br>
<textarea name="content"><%= content %></textarea>
<br>
<input type="submit" value="수정"/>
</form>
<button onclick="history.back()">이전으로</button>
<button onclick="location.href='list.jsp'">목록보기</button>
</body>
</html>
** updateOk.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "java.sql.*" %>
<%
// parameter 받아오기
request.setCharacterEncoding("utf-8"); // 한글 인코딩 반드시!
// * 이 단계에서 parameter 검증 필요하나 생략.
int uid = Integer.parseInt(request.getParameter("uid"));
String subject = request.getParameter("subject");
String content = request.getParameter("content");
%>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
final String SQL_WRITE_UPDATE
= "UPDATE test_write SET wr_subject = ?, wr_content = ? WHERE wr_uid = ?";
%>
<%
try {
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜젝션 실행
pstmt = conn.prepareStatement(SQL_WRITE_UPDATE);
pstmt.setString(1, subject);
pstmt.setString(2, content);
pstmt.setInt(3, uid);
cnt = pstmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
// 예외 처리
} finally {
// 리소스 해제
try {
if(rs != null) {rs.close();}
if(stmt != null) {stmt.close();}
if(pstmt != null) {pstmt.close();}
if(conn != null) {conn.close();}
} catch(Exception e) {
e.printStackTrace();
}
}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<% if(cnt == 0) { %>
<script>
alert('수정 실패');
history.back();
</script>
<% } else { %>
<script>
alert('수정 성공');
location.href = "view.jsp?uid=<%= uid %>";
</script>
<% } %>
15. 글 삭제
** deleteOk.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "java.sql.*" %>
<%
// parameter 받아오기
int uid = Integer.parseInt(request.getParameter("uid"));
%>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
final String SQL_WRITE_DELETE_BY_UID
= "DELETE FROM test_write WHERE wr_uid = ?";
%>
<%
try {
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜젝션 실행
pstmt = conn.prepareStatement(SQL_WRITE_DELETE_BY_UID);
pstmt.setInt(1, uid);
cnt = pstmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
// 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
} finally {
// 리소스 해제
try {
if(rs != null) {rs.close();}
if(stmt != null) {stmt.close();}
if(pstmt != null) {pstmt.close();}
if(conn != null) {conn.close();}
} catch(Exception e) {
e.printStackTrace();
}
}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<% if(cnt == 0) { %>
<script>
alert('삭제 실패');
history.back();
</script>
<% } else { %>
<script>
alert('삭제 성공');
location.href = "list.jsp"; <%-- 삭제후에는 list 로 가자 --%>
</script>
<% } %>
16. 페이지 코드 작성 흐름
1) Parameter 받아오기, 유효성 검증
2) 페이지에 필요한 데이터 처리, 트랜잭션
3) 결과 출력 (HTML 작성)
[Pagination (페이징)]
1. 페이징(Pagination) 구현시 고려할 요소
1) 한 페이지(Page)당 몇 개의 글 목록을 나타낼 것인가?
: SQL 함수 count(*) 사용
2) 한 페이징(Pagination)당 몇 개의 페이지(Page)를 표현할 것인가?
: 몇 번째(fromRow)부터 몇 개(pageRows)를 SELECT할 것인가?
> SQL ROWNUM 사용
2. Pagination(페이징) 구현을 위해 많은 데이터가 필요, 개수 늘리기
-- 저장되어 있는 데이터 확인
SELECT * FROM test_write;
-- 다량의 데이터 필요
SELECT * FROM test_write ORDER BY wr_uid DESC;
INSERT INTO test_write(wr_uid, wr_subject, wr_content, wr_name)
SELECT test_write_seq.nextval, wr_subject, wr_content, wr_name FROM test_write;
3. ROWNUM
: 몇 번째(fromRow)부터 몇 개(pageRows)을 구현하기 위해서는
SELECT로 추출하는 ROW들에게 순서가 필요,
오라클에서는 ROWNUM 객체를 이용하면 Row들에게 순서대로 번호를 줄 수 있음
-- ROWNUM 출력
SELECT ROWNUM, wr_name FROM test_write;
-- 페이지당 1부터 5 추출
SELECT ROWNUM, wr_name FROM test_write
WHERE ROWNUM >= 1 AND ROWNUM < 1 + 5;
4. list.jsp에서 pagination.jsp를 include하는 구조
** list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>
<%
int curPage = 1; // 현재 페이지 (디폴트 1 page)
// 현재 몇 페이지인지 parameter 받아 오기 + 검증
String pageParam = request.getParameter("page");
if(pageParam != null && !pageParam.trim().equals("")) {
try {
// 1이상의 자연수 이어야 한다
int p = Integer.parseInt(pageParam);
if(p > 0) {curPage = p;}
} catch (NumberFormatException e) {
// page parameter 오류는 별도의 exception 처리 안함
}
}
%>
<%!
// JDBC 관련 기본 객체변수
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // SELECT 결과, executeQuery()
int cnt = 0; // DML 결과, executeUpdate()
// Connection 에 필요한 값 세팅
final String DRIVER = "oracle.jdbc.driver.OracleDriver";
final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
final String USERID = "scott0316";
final String USERPW = "tiger0316";
%>
<%!
// 쿼리문 준비
//final String SQL_WRITE_SELECT
// = "SELECT * FROM test_write ORDER BY wr_uid DESC";
//페이징
// 글 목록 전체 개수 가져오기
final String SQL_WRITE_COUNT_ALL = "SELECT count(*) FROM test_write";
// fromRow 부터 pageRows 만큼 SELECT
// (몇번째) 부터 (몇개) 만큼
final String SQL_WRITE_SELECT_FROM_ROW = "SELECT * FROM " +
"(SELECT ROWNUM AS RNUM, T.* FROM (SELECT * FROM test_write ORDER BY wr_uid DESC) T) " +
"WHERE RNUM >= ? AND RNUM < ?";
// 페이징 관련 세팅 값들
int writePages = 10; // 한 [페이징] 에 몇개의 '페이지' 를 표현할 것인가?
int pageRows = 8; // 한 '페이지' 에 몇개의 글을 리스트업 할 것인가?
int totalPage = 0; // 총 몇 '페이지' 분량인가?
%>
<%
try {
Class.forName(DRIVER);
out.println("드라이버 로딩 성공" + "<br>");
conn = DriverManager.getConnection(URL, USERID, USERPW);
out.println("conn 성공" + "<br>");
// 트랜젝션 실행
pstmt = conn.prepareStatement(SQL_WRITE_COUNT_ALL);
rs = pstmt.executeQuery();
if(rs.next()) {
cnt = rs.getInt(1); // count(*)
}
rs = pstmt.executeQuery();
pstmt.close();
totalPage = (int)Math.ceil(cnt / (double)pageRows); // 총 몇페이지 분량
int fromRow = (curPage - 1) * pageRows + 1; // 몇 번째 row 부터?
pstmt = conn.prepareStatement(SQL_WRITE_SELECT_FROM_ROW);
pstmt.setInt(1, fromRow);
pstmt.setInt(2, fromRow + pageRows);
rs = pstmt.executeQuery();
//out.println("쿼리 성공<br>");
%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>글 목록 <%= curPage %>페이지</title>
<style>
table {width: 100%;}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
<!-- 페이징 -->
<link rel="stylesheet" type="text/css" href="CSS/common.css"/>
<script src="https://kit.fontawesome.com/bb29575d31.js"></script>
</head>
<body>
<hr>
<h2>리스트 <%= curPage %>페이지</h2>
<h4><%= cnt %>개</h4> <!-- 전체 글 개수 -->
<table>
<tr>
<th>row</th> <!-- row 번호 -->
<th>UID</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>등록일</th>
</tr>
<%
while (rs.next()) {
out.println("<tr>");
int rnum = rs.getInt("rnum"); // rownum 받아오기
int uid = rs.getInt("wr_uid");
String subject = rs.getString("wr_subject");
String name = rs.getString("wr_name");
int viewcnt = rs.getInt("wr_viewcnt");
Date d = rs.getDate("wr_regdate"); // java.sql
Time t = rs.getTime("wr_regdate"); // java.sql
String regdate = "";
if(d != null) {
regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " "
+ new SimpleDateFormat("hh:mm:ss").format(t);
}
out.println("<td>" + rnum + "</td>"); // rownum 찍어주기
out.println("<td>" + uid + "</td>");
out.println("<td><a href='view.jsp?uid=" + uid + "&page=" + curPage + "'>" + subject + "</a></td>");
out.println("<td>" + name + "</td>");
out.println("<td>" + viewcnt + "</td>");
out.println("<td>" + regdate + "</td>");
out.println("</tr>");
} // end while
%>
</table>
<br>
<button onclick="location.href='write.jsp'">신규등록</button>
<%
} catch(Exception e) {
e.printStackTrace();
// * 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
} finally {
// 리소스 해제
try {
if(rs != null) {rs.close();}
if(stmt != null) {stmt.close();}
if(pstmt != null) {pstmt.close();}
if(conn != null) {conn.close();}
} catch(Exception e) {
e.printStackTrace();
}
}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<%-- 페이지 --%>
<%-- 페이징 --%>
<jsp:include page="pagination.jsp">
<jsp:param value="<%= writePages %>" name="writePages"/>
<jsp:param value="<%= totalPage %>" name="totalPage"/>
<jsp:param value="<%= curPage %>" name="curPage"/>
</jsp:include>
</body>
</html>
** pagination.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// 한페이징에 표시될 페이지수 --> writePages
// 총 페이지수 --> totalPage
// 현재 페이지 --> curPage
int writePages = Integer.parseInt(request.getParameter("writePages"));
int totalPage = Integer.parseInt(request.getParameter("totalPage"));
int curPage = Integer.parseInt(request.getParameter("curPage"));
// ※ 사실 위 단계에서도 파라미터 검증 필요하다
// 위 url에 추가로 붙어야 할 것들. (옵션)
String add = request.getParameter("add");
if(add == null){ add = ""; }
// 페이징 버튼 링크 url 주소에 넣을 문자열 준비
String url = request.getRequestURL().toString() + "?page=";
String str = ""; // 최종적으로 페이징에 나타날 HTML 문자열 <li> 태그로 구성
// 페이징에 보여질 숫자들 (시작숫자 start_page ~ 끝숫자 end_page)
int start_page = ( ( (int)( (curPage - 1 ) / writePages ) ) * writePages ) + 1;
int end_page = start_page + writePages - 1;
if (end_page >= totalPage){
end_page = totalPage;
}
//■ << 표시 여부
if(curPage > 1){
str += "<li><a href='" + url + "1" + add + "' class='tooltip-top' title='처음'><i class='fa fa-angle-double-left'></i></a></li>\n";
}
//■ < 표시 여부
if (start_page > 1)
str += "<li><a href='" + url + (start_page - 1) + add + "' class='tooltip-top' title='이전'><i class='fa fa-angle-left'></i></a></li>\n";
//■ 페이징 안의 '숫자' 표시
if (totalPage > 1) {
for (int k = start_page; k <= end_page; k++) {
if (curPage != k)
str += "<li><a href='" + url + k + add + "'>" + k + "</a></li>\n";
else
str += "<li><a href='#' class='active tooltip-top' title='현재페이지'>" + k + "</a></li>\n";
}
}
//■ > 표시
if (totalPage > end_page){
str += "<li><a href='" + url + (end_page + 1) + add + "' class='tooltip-top' title='다음'><i class='fa fa-angle-right'></i></a></li>\n";
}
//■ >> 표시
if (curPage < totalPage) {
str += "<li><a href='" + url + totalPage + add + "' class='tooltip-top' title='맨끝'><i class='fa fa-angle-double-right'></i></a></li>\n";
}
%>
<!-- top nav -->
<div class="center">
<ul class="pagination">
<%= str %>
</ul>
</div>
5. CSS 추가
** WebContent > 01page > CSS > common.css
@charset "UTF-8";
.center {
text-align: center;
}
ul.pagination{
list-style-type:none
}
ul.pagination li{
display: inline-block;
}
ul.pagination a {
color: black;
float: left;
padding: 4px 8px;
text-decoration: none;
transition: background-color .3s;
/* border: 1px solid #ddd; */
/* margin: 0 4px; */
margin: 0px;
}
ul.pagination a.active {
background-color: #4CAF50;
color: white;
border: 1px solid #4CAF50;
}
ul.pagination a:hover:not(.active) {background-color: #ddd;}
6. 세 가지 정보가 pagination에 넘어가는 것이 중요!
1) writePages : 한 페이징(pagination)에 몇 개의 페이지(page)가 표시되나?
2) totalPage : 총 몇개의 페이지(page)인가?
3) curPage : 현재 페이지는 몇 페이지(page)인가?
'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글
2020.05.25 (0) | 2020.05.25 |
---|---|
2020.05.22 (0) | 2020.05.22 |
2020.05.20 (0) | 2020.05.20 |
2020.05.19 (0) | 2020.05.19 |
2020.05.18 (0) | 2020.05.18 |