7장. 서블릿 비즈니스 로직 처리
7.1 서블릿의 비즈니스 로직 처리 방법
- 서블릿 비즈니스 처리작업 : 서블릿이 클라이언트로부터 요청을 받으면 그 요청에 대해 작업(ex. 데이터베이스 연동 작업, 다른 서버와 연동해서 데이터를 얻는 작업)을 수행하는 것
- 서블릿 비즈니스 작업 예) 웹 사이트 회원 등록/로그인/쇼핑몰 상품 주문 요청 처리 작업
<서블릿의 비즈니스 처리 과정>
1. 클라이언트로부터 요청 받기
2. 데이터베이스 연동과 같은 비즈니스 로직 처리
3. 처리 결과를 클라이언트에게 돌려 줌
7.2 서블릿의 데이터베이스 연동하기
클라이언트로부터 요청을 받으면 서블릿은 SQL문을 사용해 db에 접근하여 작업을 함. 이 과정에서 DAO, VO 클래스가 사용 됨
* DAO : DB의 data에 접근하기 위한 객체
* VO : 단순히 값 타입을 표현하기 위한 클래스
<서블릿으로 테이블의 회원 정보 조회 하는 과정>
1. 웹 브라우저가 서블릿에게 회원 정보를 요청
2. 서블릿이 요청을 받은 후 DAO 객체를 생성하여 그 내의 db 데이터를 가져오는 list() 메서드를 호출
3. list()에서 다시 데이터베이스와 연결하는 connDB() 메서드를 호출하여 DB와 연결한 후 SQL문을 실행 해 회원 정보를 조회
4. 조회된 회원 정보를 VO 속성에 설정한 후 다시 ArrayList에 저장
5. ArrayList를 다시 메서드를 호출한 서블릿으로 반환한 후 ArrayList의 VO를 차례대로 가져와 회원 정보를 HTML 태그의 문자열로 만듦
6. 만들어진 HTML 태그를 웹 브라우저로 전송해서 회원 정보를 출력
~실제 작동 방법~
1) 테이블에 데이터 넣기
2) 오라클db와 연동하는데 필요한 드라이버인 ojdbc6.jar를 다운받아 프로젝트의 /WebContent/WEB-INF/lib 폴더에 복붙
3) DAO, Servlet, VO 클래스 생성
4) 위의 회원 정보 조회 과정 2~6 실행
1) SQL Developer실행 > 왼쪽에 + 클릭해 새 접속.. > 연결 정보 입력 후 접속 > 테이블 생성, 필드 주입하는 SQL문 작성
create table t_member(
id varchar2(10) primary key,
pwd varchar2(10),
name varchar2(50),
email varchar2(50),
joinDate date default sysdate
);
insert into t_member
values('hong','1212','홍길동','hong@gmail.com',sysdate);
insert into t_member
values('lee','1212','이순신','lee@gmail.com',sysdate);
insert into t_member
values('kim','1212','김유신','kim@gmail.com',sysdate);
commit;
select * from t_member
* sysdate : 현재 날짜 자료형
* commit : SQL Developer에서 테이블에 정보 추가 후 반드시 커밋을 해줘야 영구적으로 반영 됨
* 참고) 테이블에 insert할 때 '테이블스페이스 'USERS'에 대한 권한이 없습니다.' 라는 오류 발생할 시
sqlplus에서 로그인하고
ALTER USER C##scott DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS; 입력
<4 코드>
MemberServlet.java
@WebServlet("/member")
public class MemberServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
MemberDAO dao=new MemberDAO(); //SQL문으로 DB를 조회하는 클래스인 MemberDAO 객체 생성
List<MemberVO> list=dao.listMembers(); //회원 정보 조회
out.print("<html><body>");
out.print("<table border=1><tr align='center' bgcolor='lightgreen'>");
out.print("<td>아이디</td><td>비밀번호</td><td>이름</td><td>이메일</td><td>가입일</td></tr>");
for(int i=0;i<list.size();i++) {
MemberVO memberVO=list.get(i);
String id=memberVO.getId();
String pwd=memberVO.getPwd();
String name=memberVO.getName();
String email=memberVO.getEmail();
Date joinDate=memberVO.getJoinDate();
out.print("<tr><td>"+id+"</td><td>"+
pwd+"</td><td>"+
name+"</td><td>"+
email+"</td><td>"+
joinDate+"</td></tr>");
}
out.print("</table></body></html>");
}
}
MemberDAO.java
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 = "scott";
private static final String pwd = "tiger";
private Statement stmt;
private Connection con;
public List<MemberVO> listMembers() {
List<MemberVO> list=new ArrayList<MemberVO>();
try {
connDB(); //db연결
String query="select * from t_member";
System.out.println(query);
ResultSet rs=stmt.executeQuery(query); //sql문으로 db조회(느리다는 단점으로 추후 수정예정)
while(rs.next()) {
String id=rs.getString("id"); //조회한 레코드의 각 컬럼 값을 받아 옴
String pwd = rs.getString("pwd");
String name = rs.getString("name");
String email = rs.getString("email");
Date joinDate = rs.getDate("joinDate");
MemberVO vo = new MemberVO(); //각 컬럼 값을 vo객체의 속성에 넣기
vo.setId(id);
vo.setPwd(pwd);
vo.setName(name);
vo.setEmail(email);
vo.setJoinDate(joinDate);
list.add(vo); //vo객체를 리스트에 추가
}
rs.close();
stmt.close();
con.close();
}catch(Exception e){
e.printStackTrace();
}
return list; //테이블의 데이터 전부를 객체로 담아 리스트로 반환
}
private void connDB() {
try {
Class.forName(driver);
System.out.println("Oracle 드라이버 로딩 성공");
con = DriverManager.getConnection(url, user, pwd);
System.out.println("Connection 생성 성공");
stmt = con.createStatement();
System.out.println("Statement 생성 성공");
} catch (Exception e) {
e.printStackTrace();
}
}
MemberVO.java
package sec01.ex01;
import java.sql.Date;
public class MemberVO {
private String id; //t_member 테이블의 컬럼 이름과 동일한 자료형과 이름으로 속성 선언
private String pwd;
private String name;
private String email;
private Date joinDate;
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;
}
}
앞에선 MemberDAO에서 Statement 인터페이스를 이용해서 db와 연동 함
private Statement stmt;
ResultSet rs=stmt.executeQuery(query);
>> 연동할 때마다 DBMS에서 다시 SQL문을 컴파일해야 하므로 속도가 느림
>> PreparedStatement 인터페이스를 사용! => SQL문을 미리 컴파일해서 재사용하므로 훨씬 빠름
<PreparedStatement 인터페이스 특징>
- Statement 인터페이스를 상속하므로 지금까지 사용한 메서드를 그대로 사용
- 실행하려는 SQL문에 '?'를 넣을 수 있음
- 이미 컴파일된 SQL문을 DBMS에 전달하여 성능 향상
(cf. Statement : DBMS에 전달하는 SQL문은 단순한 문자열 / DBMS는 이 문자열을 DBMS가 이해할 수 있도록 컴파일&실행)
7.3 DataSource 이용해 데이터베이스 연동하기
- 앞 절에서는 웹 애플리케이션이 필요할 때마다 db에 연결하여 작업하는 방식 >> db 연결에 시간 많이 소요
- 웹 애플리케이션이 실행됨과 동시에 연동할 db와의 연결을 미리 설정해 두고 필요할 때마다 이 연결을 이용해 빠르게 데이터 베이스와 연동하여 작업
- 커넥션풀 : 미리 db와 연결시킨 상태를 유지하는 기술
- 즉, 애플리케이션 실행 시 미리 ConnectionPool 객체를 생성 후 연결을 맺고 db연동 작업 발생 시 이 객체를 이용해 작업
- 톰캣 컨테이너는 자체적으로 ConnectionPool 기능을 제공, 톰캣 실행 시 톰캣은 설정 파일에 설정된 DB 정보를 이용해 미리 데이터베이스와 연결하여 ConnectionPool 객체를 생성 후에 이 객체 메서드를 호출하여 연동 작업
- 실제 웹 애플리케이션에서는 Java SE에서 제공하는 javax.sql.DataSource 클래스를 이용해 ConnectionPool 객체를 구현
- ConnectionPool 객체에 접근할 때는 JNDI 이용
JNDI : 필요한 자원을 키/값 쌍으로 저장한 후 필요할 때 키를 이용해 값을 얻는 방법
JNDI의 사용 예)
- 웹브라우저에서 name/value 쌍으로 전송한 후 서블릿에서 getParameter(name)로 값을 가져올 때
- 해시맵/해시테이블
- 웹 브라우저에서 도메인 네임으로 DNS 서버에 요청할 경우 도메인 네임에 대한 IP 주소를 가져올 때
톰캣 컨테이너가 ConnectionPool 객체를 생성하면 이 객체에 대한 JNDI 이름(key)을 미리 설정, 후에 연동작업 시 이 키로 접근하여 작업
<톰캣의 DataSource 설정 및 사용 방법>
1. JDBC 드라이버와 ConnectionPool 기능 관련 jar 파일(DBCP 라이브러리)을 /WEB-INF/lib 폴더에 설치
2. CATALINA_HOME/context.xml에 Connection 객체 생성 시 연결할 db정보를 JNDI로 설정
3. DAO 클래스에서 DB와 연동 시 미리 설정한 JNDI라는 이름으로 DB와 연결해서 작업
2는 Servers>Tomcat ~ > context.xml에 <Context> 태그 내 밑에 소스 입력
<Resource
name="jdbc/oracle"
auth="Container"
type="javax.sql.DataSource"
driverClassName = "oracle.jdbc.driver.OracleDriver"
url = "jdbc:oracle:thin:@localhost:1521:XE"
username = "C##scott"
password = "tiger"
maxActive="50"
maxWait="-1"
/>
* name의 jdbc/orcle로 DataSource에 접근
* 오라클 데이터베이스를 연결할 때 driverClassName, url, username, password만 주로 변경해서 설정
* <Resource ~~ /> 로 해야함에 주의
<ConnectionPool로 연결할 데이터베이스 속성>
- name : DataSource에 대한 JNDI 이름
- auth : 인증 주체
- driverClassName : 드라이버 클래스 이름
- factory : ConnectionPool 생성 클래스 이름
- maxActive : 동시에 최대로 db에 연결할 수 있는 connection 수
- maxIdle : 동시에 idle 상태로 대기할 수 있는 최대 수
- maxWait : 새로운 연결이 생길 때까지 기다릴 수 있는 최대 시간
- user/password : 데이터베이스 접속 id/비번
- type : 데이터베이스 종류별 DataSource
- url : 접속할 데이터베이스 주소와 포트번호 및 SID
private DataSource dataFactory;
public MemberDAO() {
try {
Context ctx=new InitialContext();
Context envContext=(Context) ctx.lookup("java:/comp/env"); //JNDI에 접근하기 위해 기본 경로를 지정
dataFactory=(DataSource) envContext.lookup("jdbc/oracle");
//톰캣 context.xml에 설정한 name 값을 이용해 톰캣이 미리 연결한 DataSource를 받아 옴
}catch(Exception e) {
e.printStackTrace();
}
}
public List<MemberVO> listMembers() {
List<MemberVO> list=new ArrayList<MemberVO>();
try {
con=dataFactory.getConnection(); //DataSource를 이용해 db에 연결
...
이전 코드와 다르게 db연결을 위한 변수, 메서드 conn()가 필요 없다.
7.4 DataSource 이용해 회원 정보 등록하기
<PrepareStatement에서 insert문 사용하는 방법>
- ?는 순서대로 대응
- 각 ?에 대응하는 값을 지정하기 위해 PrepareStatement의 setter를 이용
- sett()의 첫번째 인자는 '?'의 순서를 지정
- ?는 1부터 시작
- insert,delete, update문은 executeUpdate() 메서드를 호출해야 함
DAO에서 insert문 사용하기
public void addMember(MemberVO memberVO) {
try {
con=dataFactory.getConnection();
String id = memberVO.getId();
String pwd = memberVO.getPwd();
String name = memberVO.getName();
String email = memberVO.getEmail();
String query = "insert into t_member";
query+="(id, pwd, name, email)";
query+="values(?,?,?,?)";
pstmt=con.prepareStatement(query);
pstmt.setString(1, id);
pstmt.setString(2, pwd);
pstmt.setString(3, name);
pstmt.setString(4, email);
pstmt.executeUpdate();
pstmt.close();
}catch(Exception e) {
e.printStackTrace();
}
}
7.5 회원 정보 삭제하기
dao.delMember(id);
&
String query="delete from t_member"+" where id=?" ... 로 삭제 로직 구현