방명록 구현에 관련된 클래스
DAO
서비스 클래스
JDBC Connection 관련
|
서비스 클래스는 ConnectionProvider와 MessageDao를 이용해서 필요한 기능을 구현한다. 다음과 같이 ConnectionProvider에서 Connection을 구하고 MessageDao를 알맞게 메서드를 호출하는 방식을 작성한다.
public void deleteMessage(int messageId, String password) {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
MessageDao messageDao = MessageDao.getInstance();
Message message = messageDao.select(conn, messageId);
if (message == null) {
throw new MessageNotFoundException("메시지 없음");
}
if (!message.matchPassword(password)) {
throw new InvalidPassowrdException("bad password");
}
messageDao.delete(conn, messageId);
conn.commit();
} catch (SQLException ex) {
JdbcUtil.rollback(conn);
throw new ServiceException("삭제 실패:" + ex.getMessage(), ex);
} catch (InvalidPassowrdException | MessageNotFoundException ex) {
JdbcUtil.rollback(conn);
throw ex;
} finally {
JdbcUtil.close(conn);
}
}
}
1.1 데이터 베이스와 테이블 생성
create database guestbook;
grant all privileges on guestbook.* to 'root'@'localhost';
create table guestbook_message (
message_id int not null auto_increment primary key,
guest_name varchar(50) not null,
password varchar(10) not null,
message text not null
) engine=InnoDB default character set = utf8
auto_increment 자동으로 값이 1증가하는 컬럼으로 insert쿼리를 수행할 때 값을 지정하지 않으면 1이 증가된 값이 삽입된다.
1.2 이클립스 프로젝트 생성과 필요 모듈 복사
WEB-INF / lib 폴더에 복사.
1.3 jdbcUtil과 ConnectionProvider 클래스 작성
ConnectionProvider
package jdbc.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionProvider {
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(
"jdbc:apache:commons:dbcp:guestbook");
}
}
jdbcUtil
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtil {
public static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
}
}
}
public static void close(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex) {
}
}
}
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
}
}
}
public static void rollback(Connection conn) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
}
}
}
}
1.4 커넥션 풀 설정을 위한 DBCPInit 클래스 구현과 web.xml 설정
package jdbc;
import java.sql.DriverManager;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDriver;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class DBCPInit extends HttpServlet {
@Override
public void init() throws ServletException {
loadJDBCDriver();
initConnectionPool();
}
private void loadJDBCDriver() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ex) {
throw new RuntimeException("fail to load JDBC Driver", ex);
}
}
private void initConnectionPool() {
try {
String jdbcUrl =
"jdbc:mysql://localhost:3305/guestbook?" +
"useUnicode=true&characterEncoding=utf8";
String username = "root";
String pw = "1234";
ConnectionFactory connFactory =
new DriverManagerConnectionFactory(jdbcUrl, username, pw);
PoolableConnectionFactory poolableConnFactory =
new PoolableConnectionFactory(connFactory, null);
poolableConnFactory.setValidationQuery("select 1");
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setTimeBetweenEvictionRunsMillis(1000L * 60L * 5L);
poolConfig.setTestWhileIdle(true);
poolConfig.setMinIdle(4);
poolConfig.setMaxTotal(50);
GenericObjectPool<PoolableConnection> connectionPool =
new GenericObjectPool<>(poolableConnFactory, poolConfig);
poolableConnFactory.setPool(connectionPool);
Class.forName("org.apache.commons.dbcp2.PoolingDriver");
PoolingDriver driver =
(PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
driver.registerPool("guestbook", connectionPool);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
<servlet>
<servlet-name>DBCPInit</servlet-name>
<servlet-class>jdbc.DBCPInit</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
1.5 Message 클래스 작성
package guestbook.model;
public class Message {
private int id;
private String guestName;
private String password;
private String message;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGuestName() {
return guestName;
}
public void setGuestName(String guestName) {
this.guestName = guestName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean hasPassword() {
return password != null && !password.isEmpty();
}
public boolean matchPassword(String pwd) {
return password != null && password.equals(pwd);
}
}
1.6 MessageDao 클래스 구현
package guestbook.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import guestbook.model.Message;
import jdbc.JdbcUtil;
public class MessageDao {
private static MessageDao messageDao = new MessageDao();
public static MessageDao getInstance() {
return messageDao;
}
private MessageDao() {}
public int insert(Connection conn, Message message) throws SQLException {
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(
"insert into guestbook_message " +
"(guest_name, password, message) values (?, ?, ?)");
pstmt.setString(1, message.getGuestName());
pstmt.setString(2, message.getPassword());
pstmt.setString(3, message.getMessage());
return pstmt.executeUpdate();
} finally {
JdbcUtil.close(pstmt);
}
}
public Message select(Connection conn, int messageId) throws SQLException {
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(
"select * from guestbook_message where message_id = ?");
pstmt.setInt(1, messageId);
rs = pstmt.executeQuery();
if (rs.next()) {
return makeMessageFromResultSet(rs);
} else {
return null;
}
} finally {
JdbcUtil.close(rs);
JdbcUtil.close(pstmt);
}
}
private Message makeMessageFromResultSet(ResultSet rs) throws SQLException {
Message message = new Message();
message.setId(rs.getInt("message_id"));
message.setGuestName(rs.getString("guest_name"));
message.setPassword(rs.getString("password"));
message.setMessage(rs.getString("message"));
return message;
}
public int selectCount(Connection conn) throws SQLException {
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery("select count(*) from guestbook_message");
rs.next();
return rs.getInt(1);
} finally {
JdbcUtil.close(rs);
JdbcUtil.close(stmt);
}
}
public List<Message> selectList(Connection conn, int firstRow, int endRow)
throws SQLException {
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(
"select * from guestbook_message " +
"order by message_id desc limit ?, ?");
pstmt.setInt(1, firstRow - 1);
pstmt.setInt(2, endRow - firstRow + 1);
rs = pstmt.executeQuery();
if (rs.next()) {
List<Message> messageList = new ArrayList<Message>();
do {
messageList.add(makeMessageFromResultSet(rs));
} while (rs.next());
return messageList;
} else {
return Collections.emptyList();
}
} finally {
JdbcUtil.close(rs);
JdbcUtil.close(pstmt);
}
}
public int delete(Connection conn, int messageId) throws SQLException {
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(
"delete from guestbook_message where message_id = ?");
pstmt.setInt(1, messageId);
return pstmt.executeUpdate();
} finally {
JdbcUtil.close(pstmt);
}
}
}
1.7 서비스 클래스의 구현
서비스 클래스
- GetMessageListService : 요청한 페이지 번호에 포함된 메시지 목록을 구한다.
package guestbook.service;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import guestbook.dao.MessageDao;
import guestbook.model.Message;
import jdbc.JdbcUtil;
import jdbc.connection.ConnectionProvider;
public class GetMessageListService {
private static GetMessageListService instance = new GetMessageListService();
public static GetMessageListService getInstance() {
return instance;
}
private GetMessageListService() {
}
private static final int MESSAGE_COUNT_PER_PAGE = 3;
public MessageListView getMessageList(int pageNumber) {
Connection conn = null;
int currentPageNumber = pageNumber;
try {
conn = ConnectionProvider.getConnection();
MessageDao messageDao = MessageDao.getInstance();
int messageTotalCount = messageDao.selectCount(conn);
List<Message> messageList = null;
int firstRow = 0;
int endRow = 0;
if (messageTotalCount > 0) {
firstRow =
(pageNumber - 1) * MESSAGE_COUNT_PER_PAGE + 1;
endRow = firstRow + MESSAGE_COUNT_PER_PAGE - 1;
messageList =
messageDao.selectList(conn, firstRow, endRow);
} else {
currentPageNumber = 0;
messageList = Collections.emptyList();
}
return new MessageListView(messageList,
messageTotalCount, currentPageNumber,
MESSAGE_COUNT_PER_PAGE, firstRow, endRow);
} catch (SQLException e) {
throw new ServiceException("목록 구하기 실패: " + e.getMessage(), e);
} finally {
JdbcUtil.close(conn);
}
}
}
- WriteMessageService : 메시지를 작성하는 기능을 제공한다.
package guestbook.service;
import java.util.List;
import guestbook.model.Message;
public class MessageListView {
private int messageTotalCount;
private int currentPageNumber;
private List<Message> messageList;
private int pageTotalCount;
private int messageCountPerPage;
private int firstRow;
private int endRow;
public MessageListView(List<Message> messageList, int messageTotalCount,
int currentPageNumber, int messageCountPerPage,
int startRow, int endRow) {
this.messageList = messageList;
this.messageTotalCount = messageTotalCount;
this.currentPageNumber = currentPageNumber;
this.messageCountPerPage = messageCountPerPage;
this.firstRow = startRow;
this.endRow = endRow;
calculatePageTotalCount();
}
private void calculatePageTotalCount() {
if (messageTotalCount == 0) {
pageTotalCount = 0;
} else {
pageTotalCount = messageTotalCount / messageCountPerPage;
if (messageTotalCount % messageCountPerPage > 0) {
pageTotalCount++;
}
}
}
public int getMessageTotalCount() {
return messageTotalCount;
}
public int getCurrentPageNumber() {
return currentPageNumber;
}
public List<Message> getMessageList() {
return messageList;
}
public int getPageTotalCount() {
return pageTotalCount;
}
public int getMessageCountPerPage() {
return messageCountPerPage;
}
public int getFirstRow() {
return firstRow;
}
public int getEndRow() {
return endRow;
}
public boolean isEmpty() {
return messageTotalCount == 0;
}
}
- DeleteMessageService : 작성한 메시지를 삭제하는 기능을 제공한다.
package guestbook.service;
import java.sql.Connection;
import java.sql.SQLException;
import guestbook.dao.MessageDao;
import guestbook.model.Message;
import jdbc.JdbcUtil;
import jdbc.connection.ConnectionProvider;
public class DeleteMessageService {
private static DeleteMessageService instance = new DeleteMessageService();
public static DeleteMessageService getInstance() {
return instance;
}
private DeleteMessageService() {
}
public void deleteMessage(int messageId, String password) {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
MessageDao messageDao = MessageDao.getInstance();
Message message = messageDao.select(conn, messageId);
if (message == null) {
throw new MessageNotFoundException("메시지 없음");
}
if (!message.matchPassword(password)) {
throw new InvalidPassowrdException("bad password");
}
messageDao.delete(conn, messageId);
conn.commit();
} catch (SQLException ex) {
JdbcUtil.rollback(conn);
throw new ServiceException("삭제 실패:" + ex.getMessage(), ex);
} catch (InvalidPassowrdException | MessageNotFoundException ex) {
JdbcUtil.rollback(conn);
throw ex;
} finally {
JdbcUtil.close(conn);
}
}
}
1.8 JSP에서 서비스 사용하기
메시지 목록을 보여주는 list.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<%@ page import="guestbook.model.Message"%>
<%@ page import="guestbook.service.MessageListView"%>
<%@ page import="guestbook.service.GetMessageListService"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String pageNumberStr = request.getParameter("page");
int pageNumber = 1;
if (pageNumberStr != null) {
pageNumber = Integer.parseInt(pageNumberStr);
}
GetMessageListService messageListService =
GetMessageListService.getInstance();
MessageListView viewData =
messageListService.getMessageList(pageNumber);
%>
<c:set var="viewData" value="<%= viewData %>"/>
<html>
<head>
<title>방명록 메시지 목록</title>
</head>
<body>
<form action="writeMessage.jsp" method="post">
이름: <input type="text" name="guestName"> <br>
암호: <input type="password" name="password"> <br>
메시지: <textarea name="message" cols="30" rows="3"></textarea> <br>
<input type="submit" value="메시지 남기기" />
</form>
<hr>
<c:if test="${viewData.isEmpty()}">
등록된 메시지가 없습니다.
</c:if>
<c:if test="${!viewData.isEmpty()}">
<table border="1">
<c:forEach var="message" items="${viewData.messageList}">
<tr>
<td>
메시지 번호: ${message.id} <br/>
손님 이름: ${message.guestName} <br/>
메시지: ${message.message} <br/>
<a href="confirmDeletion.jsp?messageId=${message.id}">[삭제하기]</a>
</td>
</tr>
</c:forEach>
</table>
<c:forEach var="pageNum" begin="1" end="${viewData.pageTotalCount}">
<a href="list.jsp?page=${pageNum}">[${pageNum}]</a>
</c:forEach>
</c:if>
</body>
</html>
메시지 등록을 처리하는 write.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<%@ page errorPage="errorView.jsp" %>
<%@ page import="guestbook.model.Message" %>
<%@ page import="guestbook.service.WriteMessageService" %>
<%
request.setCharacterEncoding("utf-8");
%>
<jsp:useBean id="message" class="guestbook.model.Message">
<jsp:setProperty name="message" property="*" />
</jsp:useBean>
<%
WriteMessageService writeService = WriteMessageService.getInstance();
writeService.write(message);
%>
<html>
<head>
<title>방명록 메시지 남김</title>
</head>
<body>
방명록에 메시지를 남겼습니다.
<br/>
<a href="list.jsp">[목록 보기]</a>
</body>
</html>
메시지 삭제 품을 제공하는 confirmDeletetion.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>방명록 메시지 삭제 확인</title>
</head>
<body>
<form action="deleteMessage.jsp" method="post">
<input type="hidden" name="messageId" value="${param.messageId}">
메시지를 삭제하시려면 암호를 입력하세요:<br>
암호: <input type="password" name="password"> <br>
<input type="submit" value="메시지 삭제하기">
</form>
</body>
</html>
메시지 삭제 요청을 처리하는 deleteMessage.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<%@ page import="guestbook.service.DeleteMessageService" %>
<%@ page import="guestbook.service.InvalidPassowrdException" %>
<%
int messageId = Integer.parseInt(request.getParameter("messageId"));
String password = request.getParameter("password");
boolean invalidPassowrd = false;
try {
DeleteMessageService deleteService =
DeleteMessageService.getInstance();
deleteService.deleteMessage(messageId, password);
} catch(InvalidPassowrdException ex) {
invalidPassowrd = true;
}
%>
<html>
<head>
<title>방명록 메시지 삭제함</title>
</head>
<body>
<% if (!invalidPassowrd) { %>
메시지를 삭제하였습니다.
<% } else { %>
입력한 암호가 올바르지 않습니다. 암호를 확인해주세요.
<% }%>
<br/>
<a href="list.jsp">[목록 보기]</a>
</body>
</html>
'개발' 카테고리의 다른 글
Spring Lombok 라이브러리 설치 (0) | 2020.08.19 |
---|---|
[JSP] 회원제 게시판 구현 1 : 회원 관련 기능 (0) | 2020.07.27 |
[JSP] 웹 어플리케이션의 일반적인 구성 (0) | 2020.07.27 |
[JSP] 커넥션 풀 (0) | 2020.07.26 |
[JSP] 데이터 삽입 / 조회 /정렬 / 집합 / 수정 / 삭제 / 조인 (0) | 2020.07.26 |