Jam's story
[JSP] EmpDeptList를 MVC패턴으로 구현 본문
프로젝트 처음 설정할때 해야할 것
META-INF = 자바설정관련된것
WEB-INF= 다른폴더보다 보안이 좋다.
lib에 라이브러리를 넣어놓고,
cos.jar= 파일업로드할때 외부업로드형식으로
META-INF에 context.xml에 커넥션풀을 설정하기위한
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource
name="jdbc/myoracle"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:xe"
username="scott"
password="tiger"
maxTotal="20"
maxIdle="10"
maxWaitMillis="-1"/>
</Context>
web.xml 설정
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>scottPro</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- p 415 커넥션 풀 사용하기 위해서 설정한 코딩 -->
<resource-ref>
<description>Oracle Datasource example</description>
<res-ref-name>jdbc/myoracle</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>com.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>scott.controller.DispatcherServlet</servlet-class>
<init-param>
<param-name>path</param-name>
<param-value>/days12/commandHandler.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
web.xml/DTO /Controller/properties /Handler/Service / DAO인터페이스와 daoimpl
만드는 순서는
디스패처-프로퍼티스-커맨드핸들러-핸들러-서비스 -dto-dao-jsp
1.
web_xml 수정
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>scott.controller.DispatcherServlet</servlet-class>
<init-param>
<param-name>path</param-name>
<param-value>/days12/commandHandler.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
DTO
package scott.domain;
public class DeptDTO {
private int deptno;
private String dname;
private String loc;
public DeptDTO() {
super();
}
public DeptDTO(int deptno, String dname, String loc) {
super();
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public String toString() {
return String.format("%d\t%s\t%s", deptno, dname, loc);
//return "DeptDTO [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc + "]";
}
}
EmpDTO
package scott.domain;
import java.sql.Date;
public class EmpDTO {
private int empno;
private String ename;
private String job;
private int mgr;
private Date hiredate;
private double sal;
private double comm;
private int deptno;
public EmpDTO() {
super();
}
public EmpDTO(int empno, String ename, String job, int mgr, Date hiredate, double sal, double comm, int deptno) {
super();
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.sal = sal;
this.comm = comm;
this.deptno = deptno;
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public double getComm() {
return comm;
}
public void setComm(double comm) {
this.comm = comm;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "EmpDTO [empno=" + empno + ", ename=" + ename + ", job=" + job + ", mgr=" + mgr + ", hiredate="
+ hiredate + ", sal=" + sal + ", comm=" + comm + ", deptno=" + deptno + "]";
}
}
컨트롤러인 Dispatcher
package scott.controller;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import scott.command.CommandHandler;
//@WebServlet("/DispatcherServlet")
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public DispatcherServlet() {
super();
}
//do파일과 handler가 들어가는 map 객체를 설정
private Map<String, CommandHandler> commandHandlerMap = new HashMap<String , CommandHandler>();
@Override
public void init() throws ServletException {
// DispatcherServlet 서블릿 객체로 생성될 때 초기화 작업 - init() 메서드
// WEB-INF/commandHandler.properties 파일을 읽어서 요청 URL = CommandHandler => commandHandlerMap 추가
/*
* web_xml에 path라는 이름에 commandHandler.properties 경로를 설정을 해주었는데 , 그 path 값을 가져온다 .
* <param-name>path</param-name>
* <param-value>/days12/commandHandler.properties</param-value>
*/
String path = this.getInitParameter("path");
//그 파일의 진짜 경로를 가져온다 .
String realPath = this.getServletContext().getRealPath(path); // *****
//Users\kimjm\OneDrive\문서\JspPrac\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\scottPro\days12\commandHandler.properties
//porperites는 파일의 입출력을 위한것
Properties prop = new Properties();
//저 파일의 경로를 가져와서 , 파일을 읽는다. prop 객체에 .load()메소드를 이용해서 파일을 읽어온다.
try ( FileReader fr = new FileReader(realPath) ){
prop.load( fr );
} catch (Exception e) {
throw new ServletException();
}
//KeySet은 Key의 모음이고 , <key,value>는 entryset
Iterator<Object> ir = prop.keySet().iterator();
while ( ir.hasNext() ) {
String url = (String)ir.next(); //key = .do
String commandHandlerFullName = prop.getProperty(url); //value=handler
try {
Class<?> handlerClass = Class.forName(commandHandlerFullName);
//업캐스팅을 하는이유는 -> 어떤 handler로 담길지 모르니까 commandHandler로 업캐스팅시킴
CommandHandler commandHandler = (CommandHandler)handlerClass.newInstance(); // 인스턴스 == 생성된 객체
this.commandHandlerMap.put(url, commandHandler);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 요청 URL 분석 list.do , write.do , edit.do
String requestURI = request.getRequestURI(); // /jspPro/board/list.do
// request.getRequestURL(); // http://localhost/jspPro/board/list.do
//System.out.println(requestURI); // /scottPro(webapp까지 포함되어있음)/deptemp.do
//프로젝트명부터나옴
//wepapp=> jspPro
// requestURI 컨텍스트 path 제거
String contextPath = request.getContextPath(); // /jspPro
System.out.println(contextPath); // /scottPro
//프로젝트명이 존재한다면 ,->인덱스반환
if ( requestURI.indexOf( contextPath ) == 0 ) {
//프록젝트명이 다들 다를수있으니까 프로젝트명을 제거해줌
requestURI = requestURI.substring( contextPath.length() ); // "/board/list.do" key
}
// 2. 요청 URL -> 로직 처리하는 모델 객체 ( 커맨드핸들러 ) 찾아서 + request.setAttribute()/session
CommandHandler modelHandler = this.commandHandlerMap.get( requestURI );
// commandHandler.properties => ListHandler => new ListHandler() 객체 생성 로직 처리
String viewPage = null;
try {
//핸들러는 jsp를 반환한다 .
viewPage = modelHandler.process(request, response);
} catch (Exception e) {
e.printStackTrace();
}
// 3. 응답 페이지 ( View ) 리다이렉트, 포워딩,
if ( viewPage != null ) {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
dispatcher.forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
getRequestURI()와 getRequestURL()의 차이점
request.getRequestURI(); // /jspPro/board/list.do
request.getRequestURL(); // http://localhost/jspPro/board/list.do
디스패쳐의 doGet()
1. 요청 URL 분석 list.do , write.do , edit.do
2. 요청 URL -> 로직 처리하는 모델 객체 ( 커맨드핸들러 ) 찾아서
3. 응답 페이지 ( View ) 리다이렉트, 포워딩,
System.out.println(requestURI); --> // /scottPro(webapp까지 포함되어있음)/deptemp.do
- web-inf에 properties 파일
/scottPro/deptemp.do=scott.command.ListDeptEmpHandler
CommandHandler
package scott.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface CommandHandler {
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
ListDeptEmpHandler
package scott.command;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import scott.domain.DeptDTO;
import scott.domain.EmpDTO;
import scott.service.ListDeptService;
import scott.service.ListEmpService;
public class ListDeptEmpHandler implements CommandHandler{
@Override
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
ListDeptService listDeptService = ListDeptService.getInstance();
List<DeptDTO> deptList = listDeptService.selectDeptList();
request.setAttribute( "deptList" , deptList );
String pdeptno = request.getParameter("deptno");
if ( pdeptno == null || pdeptno.equals("")) {
pdeptno = "10";
}
int deptno = Integer.parseInt(pdeptno);
ListEmpService listEmpService = ListEmpService.getInstance();
List<EmpDTO> empList = listEmpService.selectEmpList(deptno);
request.setAttribute( "empList" , empList );
request.setAttribute( "name" , "Hong gil Dong");
/*
// 포워딩 / 리다이렉트
// /jspPro/test/test00.htm
// /jspPro/test/ex01_forward.jsp 404
// String path ="/days03/test/test01.jsp";
String path ="/days04/ex01_forward.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
dispatcher.forward(request, response);
*/
return "/days12/list.jsp";
}
}
NullHandler implements CommandHandler
package scott.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class NullHandler implements CommandHandler{
@Override
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return null;
}
}
ListDeptService
package scott.service;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import javax.naming.NamingException;
import com.util.ConnectionProvider;
import com.util.JdbcUtil;
import scott.domain.DeptDTO;
import scott.persistence.DeptDAO;
public class ListDeptService {
// 1. 싱글톤
private ListDeptService() {}
private static ListDeptService instance = new ListDeptService();
public static ListDeptService getInstance() {
return instance;
}
public List<DeptDTO> selectDeptList(){
Connection con = null;
try {
con = ConnectionProvider.getConnection();
DeptDAO dao = DeptDAO.getInstance();
List<DeptDTO> list = null;
list = dao.selectDeptList(con);
return list;
} catch (NamingException | SQLException e) {
//e.printStackTrace(); syso("ListService.select() 에러 : ")
throw new RuntimeException(e);
} finally {
JdbcUtil.close(con);
}
}
}
ListEmpService
package scott.service;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import javax.naming.NamingException;
import com.util.ConnectionProvider;
import com.util.JdbcUtil;
import scott.domain.EmpDTO;
import scott.persistence.EmpDAO;
public class ListEmpService {
// 1. 싱글톤
private ListEmpService() {}
private static ListEmpService instance = new ListEmpService();
public static ListEmpService getInstance() {
return instance;
}
public List<EmpDTO> selectEmpList(int deptno){
Connection con = null;
try {
con = ConnectionProvider.getConnection();
EmpDAO dao = EmpDAO.getInstance();
List<EmpDTO> list = null;
list = dao.selectEmpList(con, deptno);
return list;
} catch (NamingException | SQLException e) {
//e.printStackTrace(); syso("ListService.select() 에러 : ")
throw new RuntimeException(e);
} finally {
JdbcUtil.close(con);
}
}
}
DeptDAO
package scott.persistence;
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 javax.naming.NamingException;
import com.util.ConnectionProvider;
import scott.domain.DeptDTO;
public class DeptDAO{
// 1. 싱글톤
private DeptDAO() {}
private static DeptDAO instance = new DeptDAO();
public static DeptDAO getInstance() {
return instance;
}
// 2. 부서 정보 반환하는 메서드
public List<DeptDTO> selectDeptList(Connection con) throws SQLException, NamingException {
ArrayList<DeptDTO> deptList = null;
Connection conn = ConnectionProvider.getConnection();
PreparedStatement pstmt = null;
String sql = "SELECT deptno, dname, loc "
+" FROM dept";
int deptno;
String dname, loc;
try{
pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
if( rs.next() ){
deptList = new ArrayList<DeptDTO>();
do{
deptno = rs.getInt("deptno");
dname = rs.getString("dname");
loc = rs.getString("loc");
DeptDTO dto = new DeptDTO(deptno, dname, loc);
deptList.add(dto);
}while( rs.next() );
} // if
pstmt.close();
rs.close();
conn.close();
}catch(Exception e){
e.printStackTrace();
}
return deptList;
} //
} // class
EmpDAO
package scott.persistence;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.naming.NamingException;
import scott.domain.EmpDTO;
public class EmpDAO{
// 1. 싱글톤
private EmpDAO() {}
private static EmpDAO instance = new EmpDAO();
public static EmpDAO getInstance() {
return instance;
}
// 2. 부서 정보 반환하는 메서드
public List<EmpDTO> selectEmpList(Connection conn, int deptno) throws SQLException, NamingException {
ArrayList<EmpDTO> empList = null;
PreparedStatement pstmt = null;
String sql = "SELECT * "
+" FROM emp "
+ " WHERE deptno = ? "
+ " ORDER BY ename ASC ";
try{
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, deptno );
ResultSet rs = pstmt.executeQuery();
int empno;
String ename, job;
int mgr;
Date hiredate; // java.sql.Date String
double sal, comm;
//int deptno;
if( rs.next() ){
empList = new ArrayList<EmpDTO>();
do{
empno = rs.getInt("empno");
ename = rs.getString("ename");
job = rs.getString("job");
mgr = rs.getInt("mgr");
hiredate = rs.getDate("hiredate");
sal = rs.getDouble("sal");
comm = rs.getDouble("comm");
deptno = rs.getInt("deptno");
EmpDTO dto = new EmpDTO(empno, ename, job
, mgr, hiredate, sal, comm, deptno);
empList.add(dto);
}while( rs.next() );
} // if
pstmt.close();
rs.close();
conn.close();
}catch(Exception e){
e.printStackTrace();
}
return empList;
} //
} // class
list.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String contextPath = request.getContextPath();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2022. 6. 29. - 오후 2:42:55</title>
<link rel="shortcut icon" type="image/x-icon" href="../images/SiSt.ico">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style>
table{
width:100%;
min-width: 700px;
}
table, th, td {
border: solid 1px gray;
}
</style>
</head>
<body>
<h3>/days00/scott/list.jsp</h3>
<br>
<br>
<select id="selDept" name="deptno">
<c:forEach items="${ deptList }" var="dto">
<option value="${ dto.deptno }">${ dto.dname} </option>
</c:forEach>
</select>
<br>
<br>
<table>
<thead>
<tr>
<th><input type="checkbox" id="ckbAll" name="ckbAll">전체 선택</th>
<th>empno</th>
<th>ename</th>
<th>job</th>
<th>mgr</th>
<th>hiredate</th>
<th>sal</th>
<th>comm</th>
<th>deptno</th>
</tr>
</thead>
<tbody>
<c:if test="${ not empty empList }">
<c:forEach items="${ empList }" var="dto" varStatus="status">
<tr>
<td><input type="checkbox" name="empchk" data-empno="${ dto.empno }" id="empno-${ dto.empno }">${ status.count }</td>
<td>${ dto.empno }</td>
<td>${ dto.ename }</td>
<td>${ dto.job }</td>
<td>${ dto.mgr }</td>
<td>${ dto.hiredate }</td>
<td>${ dto.sal }</td>
<td>${ dto.comm }</td>
<td>${ dto.deptno }</td>
</tr>
</c:forEach>
</c:if>
<c:if test="${ empty empList }">
<tr>
<td colspan="9"> employee does not exist.</td>
</tr>
</c:if>
</tbody>
<tfoot>
<tr>
<td colspan="9" style="text-align: center">
<button id="checked_empno">확인</button>
</td>
</tr>
</tfoot>
</table>
<script>
// http://localhost/jspPro/test/test00.htm?deptno=30
$("#selDept").change(function(event) {
// BOM
location.href="<%= contextPath %>/deptemp.do?deptno=" + $(this).val();
})
// jQuery + EL
// http://localhost/jspPro/test/test00.htm?deptno=30
$("#selDept").val( "${ empty param.deptno ? 10 : param.deptno }" );
$("#ckbAll").change(function(event) {
$("table tbody tr")
.find("td:first-child :checkbox")
.prop("checked", $(this).prop("checked"));
})
$("#checked_empno").on("click", function (){
var empnos = [];
$("table tbody :checked").each( function (i, element){
var empno = $(this).parent().next().html();
empnos.push( empno );
});
location.href="<%= contextPath %>/days12/ex01_finish.jsp?empno="+ empnos.join("&empno=");
});
</script>
</body>
</html>
싱글톤 패턴을 쓰는 이유
고정된 메모리 영역을 얻으면서 한번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지할 수 있음
또한 싱글톤으로 만들어진 클래스의 인스턴스는 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다.
DBCP(DataBase Connection Pool)처럼 공통된 객체를 여러개 생성해서 사용해야하는 상황에서 많이 사용.
(쓰레드풀, 캐시, 대화상자, 사용자 설정, 레지스트리 설정, 로그 기록 객체등)
안드로이드 앱 같은 경우 각 액티비티나 클래스별로 주요 클래스들을 일일이 전달하기가 번거롭기 때문에 싱글톤 클래스를 만들어 어디서나 접근하도록 설계하는 것이 편하기 때문...
+ 인스턴스가 절대적으로 한개만 존재하는 것을 보증하고 싶을 경우 사용.
+ 두 번째 이용시부터는 객체 로딩 시간이 현저하게 줄어 성능이 좋아지는 장점!
출처: https://jeong-pro.tistory.com/86 [기본기를 쌓는 정아마추어 코딩블로그:티스토리]
'JSP' 카테고리의 다른 글
[JSP] JSON (0) | 2022.07.01 |
---|---|
[JSP] AJAX (0) | 2022.06.30 |
[JSP] ServletContextListener 인터페이스 활용 방법 (0) | 2022.06.24 |
[JSP] MVC 패턴 (0) | 2022.06.23 |
[JSP] JSTL (0) | 2022.06.23 |