JSP 5. 서블릿 비즈니스 로직 처리 (Servlet)
서블릿 비즈니스 로직 처리
서블릿의 비즈니스 처리 작업
- 서블릿이 클라이언트로 부터 요청을 받으면 그 요청에 대해 작업을 수행하는 것.
- 웹 프로그램에서 대부분의 비즈니스 처리 작업은 DB 연동 관련 작업,
그 외에 다른 서버와 연동해서 데이터를 얻는 작업도 수행. - 서블릿의 가장 핵심 기능
1) 예
- 웹 사이트 회원 등록 요청 처리 작업
- 웹 사이트 로그인 요청 처리 작업
- 쇼핑몰 상품 주문 처리 작업
2) 처리 과정
- 클라이언트로부터 요청 받음
- DB 연동과 같은 비즈니스 로직 처리
- 처리 결과를 클라이언트에게 돌려줌.
데이터 엑세스 공통 개념
- DAO 패턴을 적용하여 비즈니스 로직과 데이터 액세스 로직을 분리하는 것이 원칙임
1) DAO (Data Access Object) 패턴
- 서비스 계층에 영향을 주지 않고 데이터 액세스 기술 파트 전담
- 비즈니스 로직을 가진 엔터프라이즈 애플리케이션이라면 데이터 엑세스 계층을
DAO 패턴으로 분리함.
2) DataSource 이용해 회원 정보 등록하기
- PreparedStatement를 이용한 insert문 사용하는 방법
- ?는 id, PWD, NAME, EMAIL에 순서대로 대응한다.
- 각 ?에 대응하는 값을 지정하기 PreparedStatement의 setter를 이용함
- setter()메서드의 첫번째 인자는 ‘?’의 순서를 지정함
- ?은 1부터 시작함
- insert, delete,update문은 executeUpdate() 메서드를 호출함.
서블릿의 데이터베이스 연동하기
- Servlet, DAO(Data Access Object), VO(Value Object), DB
- 서블릿으로 회원 정보 테이블의 정보 조회
PreparedStatement
- Statement 상속하므로 지금까지 사용한 메서드를 그대로 사용함.
- 성능을 향상시킴
- SQL문을 ‘?’을 넣을 수 있으므로 ‘?’의 값만 바꾸어 손쉽게 설정 가능하다.
순서
- 웹 브라우저로 서블릿에게 회원 정보 요청
- MemberServlet은 요청을 받음
- 1) MemberDAO 객체를 생성 후 listMembers() 메서드를 호출함
- lsitMembers()에서 connDB() 메서드로 데이터베이스 연결후 SQL문 실행함.
- 조회된 회원 정보를 MemberVO 속성에 설정한 후 ArrayList에 저장함.
- ArrayList의 MemberVO를 차례대로 가져와 회원정보를 HTML 태그 문자열로 만듦.
- 만들어진 HTML 태그를 웹 브라우저로 전송함.
1) DB Value 정리
2) DB 테이블 업데이트
3) MemberVO
package kr.co.ezenac.member;
/*
* 1 ID id varchar2 10
2 비밀번호 pwd varchar2 10
3 이름 name varchar2 50
4 이메일 email varchar2 50
5 가입일자 joinDate date
*/
import java.sql.Date;
public class MemberVO {
// t_member 테이블의 컬럼 이름과 동일한 자료형으로 속성들 선언
private String id;
private String pwd;
private String name;
private String email;
private Date joinDate;
public MemberVO() {System.out.println("MemberVO 생성자 호출");}
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public String getPwd() {return pwd;}
public void setPwd(String pwd) {this.pwd = pwd;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public Date getJoinDate() {return joinDate;}
public void setJoinDate(Date joinDate) {this.joinDate = joinDate;}
}
MemberServlet
package kr.co.ezenac.member;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class MemberServlet
*/
@WebServlet("/member")
public class MemberServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
MemberDAO dao = new MemberDAO();
// SQL문으로 조회할 MemberDAO 객체를 생성함
List<MemberVO> list = dao.listMembers();
// listMembers() 메서드로 회원 정보를 조회함
out.print("<html>");
out.print("<body>");
out.print("<table border=1><tr align='center' bgcolor='lightgreen'>");
out.print("<td>아이디</td>");
out.print("<td>비밀번호</td>");
out.print("<td>이름</td>");
out.print("<td>이메일</td>");
out.print("<td>가입일</td></tr>");
for(int i=0;i<list.size();i++) {
MemberVO memberVO = list.get(i);
// 조회한 회원 정보를 for문 <tr> 태그 이용해서 출력함
String id = memberVO.getId();
String pw = memberVO.getPwd();
String name = memberVO.getName();
String email = memberVO.getEmail();
Date joindate = memberVO.getJoinDate();
out.print("<tr><td>"+id+"</td>");
out.print("<td>"+pw+"</td>");
out.print("<td>"+name+"</td>");
out.print("<td>"+email+"</td>");
out.print("<td>"+joindate+"</td></tr>");
}
out.print("</table></html>");
out.print("</body>");
}
}
4) MemberDAO - SELECT
package kr.co.ezenac.member;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class MemberDAO {
private static final String DRIVER = "oracle.jdbc.driver.OracleDriver";
private static final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
private static final String USER = "ezen";
private static final String PWD = "0824";
private Connection conn; // 데이터베이스와 연결을 담당함
private Statement stmt; // (인파라미터가 없는) 정적 쿼리문 실행할 때 사용함
private PreparedStatement pstmt;// (인파라미터가 있는) 동적 쿼리문 실행할때 사용됨
private ResultSet rs; // select 쿼리문의 결과를 저장할 때 사용됨.
public void connDB() {
try {
Class.forName(DRIVER);
System.out.println("Oracle 드라이버 로딩 성공");
conn = DriverManager.getConnection(URL,USER,PWD);
System.out.println("Connection 생성 성공");
}catch (Exception e) {
e.printStackTrace();
}
}
// 연결 해제(자원 반납)
public void close() {
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();
}
}
public List<MemberVO> listMembers(){
List<MemberVO> list = new ArrayList<>();
connDB(); // 네가지 정보로 DB를 연결함
String query = "SELECT * FROM T_MEMBER";
System.out.println(query);
try {
pstmt = conn.prepareStatement(query); //prepareStatement()메서드에 SQL문을 전달해 객체생성
rs = pstmt.executeQuery(); // 미리 설정한 SQL문 실행
while(rs.next()) {
String id = rs.getString("ID");
String pw = rs.getString("PWD");
String name = rs.getString("NAME");
String email = rs.getString("EMAIL");
Date joinDate = rs.getDate("JOINDATE");// 조회한 레코드의 각 컬럼 값을 받아 옴.
MemberVO vo = new MemberVO();
vo.setId(id);
vo.setPwd(pw);
vo.setName(name);
vo.setEmail(email);
vo.setJoinDate(joinDate); // 각 컬럼 값을 다시 MemberVO 객체의 속성에 설정함.
list.add(vo);// 설정된 MemberVO 객체를 다시 ArrayList에 저장함.
}
close();
}catch(SQLException e) {
e.printStackTrace();
}
return list;// 조회한 레코드의 개수만큼 MemberVO 객체를 저장한 ArrayList를 반환함.
}
}
5) 결과
6) MemberDAO - INSERT
public void addMember(MemberVO memberVO) {
try {
conn = dataFactory.getConnection();// DataSource을 이용해 DB 연결
String id = memberVO.getId();
String pwd = memberVO.getPwd();
String name = memberVO.getName();
String email = memberVO.getEmail();
String query = "INSERT INTO T_member (ID,PWD,NAME,EMAIL) VALUES (?,?,?,?)";
System.out.println("PrepareStatement : "+query);
pstmt = conn.prepareStatement(query);
pstmt.setString(1,id); // insert문의 ?에 순서대로 회원정보 세팅
pstmt.setString(2,pwd);
pstmt.setString(3,name);
pstmt.setString(4,email);
pstmt.executeUpdate(); // 회원정보 테이블의 추가됨
close();
}catch(Exception e) {
e.printStackTrace();
}
}
7) 결과
8) MemberDAO - DELETE
public void delMember(String id) {
try {
conn = dataFactory.getConnection();
String query = "DELETE FROM T_MEMBER WHERE ID =?";//delete 문을 문자열로 만듬
System.out.println(query);
pstmt = conn.prepareStatement(query);
pstmt.setString(1, id);// 첫번쨰 '?'에 전달된 ID를 인자로 넣음
pstmt.executeUpdate();
// delete 문실행 => 테이블에서 해당 id 회원 정보 삭제
close();
} catch(SQLException e) {
e.printStackTrace();
}
}
9) 결과
커넥션 풀(Connection pool)로 성능 개선
- DataSource 이용해 DB 연동
1) 요청이 있을 때마다 DB와 새로 연결했다가 해제함
- Connection 객체를 생성할때마다 네트워크 통신이 이루어지며 사용자인증 같은
시간이 걸리는 작업 수반됨. - 빈번한 연결과 해제는 시스템 성능에 큰 영향을 미침
2) Connection pool
- Connection 객체를 미리 생성해 풀(pool)에 넣어놓고, 요청이 있을 때 이미
생성된 Connection 객체를 가져다 사용하는 기법임. - 사용이 완료된 객체는 연결을 해제하는 것이 아니라 풀에 반납하여 필요할
때 재사용 할 수 있도록 함.
JNDI (Java Naming and Directory Interface)
- 자바 소프트웨어에서 객체나 데이터를 전체 경로를 몰라도 이름만으로 찾아
쓸수 있는 디렉터리 서비스임. - 이름과 실제 객체와의 연결은 외부의 설정 파일에서 관리함
WAS의 JNDI를 통해 커넥션 풀을 사용하는 절차
- WAS(톰캣)가 시작할 때 server.xml나 context.xml에 설정한 대로 커넥션 풀을
생성함. - JSP, Servlet 코드에서 JNDI 서버(WAS가 제공)로 부터 데이터소스 객체를 얻어옴.
- 데이터소스로부터 커넥션 객체를 가져옴.
- DB 작업 수행 함
- 모든 작업이 끝나면 커넥션 객체를 풀로 반환함.
3) 커넷션 풀을 사용하면 WAS가 시작될때마다 미리 생성한 커넥션 객체를 사용
- 웹 어플리케이션 실행 속도가 빨라짐
- 클라이언트 동시 요청이 많아지더라도 좀 더 수월하게 응답할 수 있음.
- 성능 향상 효과 큼
context.xml
...
<!--
<Manager pathname="" />
-->
<!--
name : 생성할 자원(풀) 이름
auth : 인증 주체
type : 데이터 소스로 사용할 클래스 명
driverClassName : JDBC 드라이버의 클래스명
url : 접속할 데이터베이스 주소와 포트 번호 및 SID
maxActive : 동시에 최대로 DB에 연결할 수 있는 Connection
maxWait : 새로운 연결이 생길 때까지 기다릴 수 있는 최대 시간
-->
<Resource
name="jdbc/oracle"
auth="Conainer"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:XE"
username="ezen"
password="0824"
maxActive="50"
maxWait="-1"
/>
</Context>
Connection Pool 사용
- MemberDAO
package kr.co.ezenac.member;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class MemberDAO {
private Connection conn;
private Statement stmt;
private PreparedStatement pstmt;
private ResultSet rs;
private DataSource dataFactory;
// 연결 해제(자원 반납)
public void close() {
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();
}
}
public MemberDAO() {
try {
// 커넥션 풀(DataSource) 얻기
/* 자바의 네이밍 서비스(JNDI)에서 이름과 실제 객체를 연결해주는 개념이 Context임
InitialContext는 네이밍 서비스를 이용하기위한 시작점임. */
Context ctx= new InitialContext(); // InitialContext 객체 생성
// java:comp/env <- 현재 웹 어플리케이션의 루트 디렉토리
// 현재 웹 어플리케이션이 사용할 수 있는 모든 자원은 java:comp/env 아래에 위치함.
Context envContext = (Context) ctx.lookup("java:comp/env");
// java:comp/env 아래에 위치한 jdbc/oracle 자원을 얻어옴
dataFactory = (DataSource) envContext.lookup("jdbc/oracle");
} catch (NamingException e) {
e.printStackTrace();
}
}
public List<MemberVO> listMembers(){
List<MemberVO> list = new ArrayList<>();
String query = "SELECT * FROM T_MEMBER";
System.out.println(query);
try {
// 커넥션 풀을 통해 연결 얻기
// 데이터소스를 통해 풀에 생성되어 있는 연결 객체를 얻어옴
conn = dataFactory.getConnection();
pstmt = conn.prepareStatement(query); //prepareStatement()메서드에 SQL문을 전달해 객체생성
// select 쿼리문을 실행할 떄 사용함.
// 조회한 레코드들의 집한인 Resultset 객체를 반환함
rs = pstmt.executeQuery(); // 미리 설정한 SQL문 실행
while(rs.next()) {
String id = rs.getString("ID");
String pw = rs.getString("PWD");
String name = rs.getString("NAME");
String email = rs.getString("EMAIL");
Date joinDate = rs.getDate("JOINDATE"); // 조회한 레코드의 각 컬럼 값을 받아 옴.
MemberVO vo = new MemberVO();
vo.setId(id);
vo.setPwd(pw);
vo.setName(name);
vo.setEmail(email);
vo.setJoinDate(joinDate); // 각 컬럼 값을 다시 MemberVO 객체의 속성에 설정함.
list.add(vo); // 설정된 MemberVO 객체를 다시 ArrayList에 저장함.
}
close();
}catch(SQLException e) {
e.printStackTrace();
}
return list;// 조회한 레코드의 개수만큼 MemberVO 객체를 저장한 ArrayList를 반환함.
}
}
댓글남기기