국비교육(22-23)

27일차(3)/jsp(5) : DB 연결 기본 세팅, DBCP 활용 (select, insert)

서리/Seori 2022. 11. 15. 23:52

27일차(3)/jsp(5) : DB 연결 기본 세팅, DBCP 활용

 

- Step02_DB 만들기(Dynamic web project)

 

 

** DB서버 연결을 위한 초기 세팅 (Connection 객체 얻어오기)

 

- webapp - WEB-INF - lib 에 .jar파일을 붙여넣기로 넣어준다!

 

 

* Tomcat Server 홈페이지에서 기본 세팅 작업

https://tomcat.apache.org/

 

- Documentation - Tomcat9.0 - JDBC DataSource - Oracle 8i, 9i & 10g 로 들어간다. : 링크

 

 

1) Context Configuration 

- 위 내용을 복사해서

 

- Servers - context.xml 안에 붙여넣으면 된다.

 (mysid 부분을 xe로 수정해준다)

 

 

2) web.xml configuration

- 2번 내용 복사

 

- Dynamic web project에서 우클릭 - Java EE Tools - Generate Deplyment Descriptor Stub

- 선택하면 아래 WEB-INF안에 web.xml이 생겨난다.

 

- web.xml 안에 방금 복사한 2번의 내용을 붙여넣어 준다.

 

 

3) Code example

 

- 3번을 복사, 참고해서 connection 객체를 얻어오는 클래스 만들기

 

 

- src-패키지 안에 <DbcpBean> 클래스 생성

package test.util;

import java.sql.Connection;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
/*
 *  [ Data Base Connection Pool Bean ]
 *  
 *  - 최초 Connection 이 필요한 시점에 미리 Connection 여러개를 얻어내서 Connection Pool 에 
 *    넣어 놓고 필요할때  가져다 쓰고 다 쓴다음 반납하는 구조로 사용한다.
 *  
 *  - Connection Pool 에서 Connection 한개를 가져오는 방법
 *  
 *    Connection conn=new DbcpBean().getConn();
 *    
 *  - 다 사용한 다음 반납하는 방법
 *  
 *    conn.close();   // close() 메소드를 호출하면 자동으로 Pool 에 반납된다.
 *    
 */
public class DbcpBean {
   //필드
   private Connection conn;
   
   //생성자
   public DbcpBean() {
      try {
         Context initContext = new InitialContext();
         Context envContext  = (Context)initContext.lookup("java:/comp/env");
         DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
         //리턴되는 Connection 객체를 필드에 저장하기
         conn = ds.getConnection();
         System.out.println("Connection 얻어오기 성공!");
      }catch(Exception e) {
         e.printStackTrace();
      }
   }
   
   //메소드
   public Connection getConn() {
      return conn;
   }
}

- 웹서버에서 Connection 값을 얻어오기 위한 설정이다.

 

 

- 커넥션 객체의 참조값을 필드에 넣어주고, getConn(); 실행.

- new DbcpBean().getConn(); 을 사용해서 커넥션 객체를 얻어오면 된다.

 

 

- 위 3가지 절차+jar파일 세팅이 모두 갖춰져야만 오류 없이 Connection 객체의 참조값을 얻어낼 수 있다.

- 이전과 다르게 지금은 불특정 다수가 사용하는 서버이기 때문에, Connection을 효율적으로 관리하고 얻어와야 한다.

- 새 jsp파일을 만들어 <%new DbcpBean();%> 해보면 정상적으로 설정되었는지 알 수 있다.

 

 

* Data Base Connection Pool Bean (DBCP Bean)

 

- Connection 객체를 필요한 시점에 미리 얻어내 두고, Pool에 넣어두었다가  가져와서 쓴다.

- Connection 이 떠다니는 풀장에서 가져다 써서 사용하고 다시 반납하는 것ㅎㅎ

 

- java의 관점에서 Bean은 객체를 말한다.

 


 

- DB연동을 위해서는 반드시 Connection객체가 필요하다.
- Connection 객체가 있어야만 PreparedStatement 를 가져올 수 있다.

- 이 서버는 불특정다수가 접속할 수 있고, 동시에 수많은 요청이 들어올 수 있기 때문에 커넥션 객체의 관리가 필요하다.
 그래서 Tomcat 서버에서 지정하는 내용에 맞추어 설정한 것!

 

**3가지 설정조건 확인하기!
1) context.xml 설정
2) web.xml 설정
3) java code example을 참고해서 DBCP Bean 만들기

 


 

- index 페이지에 저장해놓은 절대경로EL cpath+tab 으로 불러와서 경로 지정하기!

- test.member.dto / test.member.dao 패키지 만들기

 

<MemberDto>

package test.member.dto;

public class MemberDto {
	private int num;
	private String name;
	private String addr;
	
	public MemberDto() {}
	
	public MemberDto(int num, String name, String addr) {
		super();
		this.num = num;
		this.name = name;
		this.addr = addr;
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddr() {
		return addr;
	}

	public void setAddr(String addr) {
		this.addr = addr;
	}	
}

 

 

<MemberDao 틀 만들기>

package test.member.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import test.member.dto.MemberDto;
import test.util.DbcpBean;

public class MemberDao {
   //자신의 참조값을 저장할 static 필드(하나만 만들어 놓고 사용할 예정이기 때문에)
   private static MemberDao dao;
   
   //외부에서 객체 생성하지 못하도록 생성자의 접근지정자를 private 로 한다.
   private MemberDao() {}
   
   //자신의 참조값을 리턴하는 static 메소드를 제공한다.
   public static MemberDao getInstance() {
      //만일 최초 호출이라면 자신의 객체를 생성해서 static 필드에 담아둔다.
      if(dao==null) {
         dao=new MemberDao();
      }
      //필드에 있는 자신의 참조값을 리턴해 준다.
      return dao;
   }
   
   //전체 회원의 목록을 리턴하는 메소드
   public List<MemberDto> getList(){
      //회원 목록을 담을 객체 생성
      List<MemberDto> list=new ArrayList<>();
      
      //필요한 객체를 담을 지역변수를 미리 만들어 둔다. 
      Connection conn=null;
      PreparedStatement pstmt=null;
      ResultSet rs=null;
      try {
         //Connection Pool 에서 Connection 객체를 하나 얻어온다.
         conn=new DbcpBean().getConn();
         //실행할 sql 문의 뼈대 구성하기
         String sql="";
         //sql 문의 ? 에 바인딩 할게 있으면 한다.
         
         pstmt=conn.prepareStatement(sql);
         //SELECT 문을 수행하고 결과값을 받아온다.
         rs=pstmt.executeQuery();
         //반복문 돌면서 ResultSet 에서 필요한 값을 얻어낸다.
         while(rs.next()) {
            
         }
      }catch(Exception e) {
         e.printStackTrace();
      }finally {
         try {
            if(rs!=null)rs.close();
            if(pstmt!=null)pstmt.close();
            if(conn!=null)conn.close(); //Connection Pool 에 Connection 반납하기
         }catch(Exception e) {}
      }
      return list;
   }
}

 

 

- 외부에서 new MemberDao() {} 할 수 없다. 생성자에서 private MemberDao() {} 으로 막아두었기 때문에!

- 그 대신 MemberDao dao=MemberDao.getInstance(); 하면 리턴해준다.

 

- 최초로 호출되는 것이라면 값은 null이다.(필드는 선언만 되어있으므로)

- 객체를 생성해서 static 필드에 담아두면, 그 다음번에 호출될 때는 담아두었던 static 필드에 들어있는 내용을 불러온다.

 

- 이렇게 하면 dao가 어플리케이션 전역에 하나만 생성되게 된다.

- connection을 사용하는 dao가 쓸데없이 많을 필요는 없기 때문에, 하나만 만들어서 계속 쓴다.

- dao가 하나만 생성될 수 있도록 구조를 만들어둔 것이다.

 

- 필요에 따라 위의 3군데만 수정하면 되는 기본 뼈대를 만들어둠

 


 

** Eclipse 템플릿 저장

 

- Window-Preference-Java-Template-New 에 저장해두기!

 

 

- 템플릿을 불러왔을 때 수정할 위치에 커서가 위치하도록 하고싶다면?

- 아래 insert Variable 에서 cursor을 선택하면 커서가 위치할 장소를 지정할 수 있다.

 

- 이렇게 저장되고, 불러왔을 때 sql문의 위치에 커서가 놓여지게 된다.

 

- 이후에 MemberDao 틀이 필요할 경우 dbselect + ctrl+space 로 편하게 불러올 수 있다.

 


 

<MemberDao>

- SELECT 문 활용해서 DB에서 데이터 가져오기

package test.member.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import test.member.dto.MemberDto;
import test.util.DbcpBean;

public class MemberDao {
	//자신의 참조값을 저장할 static 필드(하나만 만들어놓고 사용할 예정이기 때문에)
	private static MemberDao dao;
	
	//외부에서 객체 생성하지 못하도록 생성자의 접근지정자를 private로 한다.
	private MemberDao() {}
	
	//자신의 참조값을 리턴하는 static 메소드를 제공한다.
	public static MemberDao getInstance() {
		//만일 최초 호출이라면 자신의 객체를 생성해서 static 필드에 담아둔다.
		if(dao==null) {
			dao=new MemberDao();
		}
		//필드에 있는 자신의 참조값을 리턴해 준다.
		return dao;
	}
	
	//전체 회원의 목록을 리턴하는 메소드
	public List<MemberDto> getList(){
		//회원 목록을 담을 객체 생성
		List<MemberDto> list=new ArrayList<>();
		
		//필요한 객체를 담을 지역변수를 미리 만들어둔다.
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//Connection Pool에서 Connection 객체를 하나 얻어온다.
			conn=new DbcpBean().getConn();
			//실행할 sql문의 뼈대 구성하기
			String sql="SELECT num, name, addr"
					+" FROM member"
					+" ORDER BY num ASC";
			//sql문의 ?에 바인딩할게 있으면 한다.
			
			pstmt=conn.prepareStatement(sql);
			//SELECT문을 수행하고 결과값을 받아온다.
			rs=pstmt.executeQuery();
			//반복문 돌면서 resultSet에서 필요한 값을 얻어낸다.
			while(rs.next()) {
				//커서가 위치한 곳에서 회원 한명의 정볼르 얻어내서 MemberDto 객체에 담은 후
				MemberDto dto=new MemberDto();
				dto.setNum(rs.getInt("num"));
				dto.setName(rs.getString("name"));
				dto.setAddr(rs.getString("addr"));
				//ArrayList 객체에 누적시킨다.
				list.add(dto);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null)rs.close();
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close(); //Connection Pool에 Connection 반납하기				
			} catch (Exception e2) {}						
		}	
		return list;		
	}	
}

 

 

 

- member 폴더안에 <list.jsp> 만들기

<%@page import="test.member.dto.MemberDto"%>
<%@page import="java.util.List"%>
<%@page import="test.member.dao.MemberDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//MemberDao 객체의 참조값을 얻어와서
	MemberDao dao=MemberDao.getInstance();
	//회원목록을 얻어온다.
	List<MemberDto> list=dao.getList();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/member/list.jsp</title>
</head>
<body>
	<div class="container">
		<table>
			<thead>
				<tr>
					<th>번호</th>
					<th>이름</th>
					<th>주소</th>
				</tr>		
			</thead>
			<tbody>
			<%for(MemberDto tmp:list){ %>
				<tr>
					<td><%=tmp.getNum() %></td>
					<td><%=tmp.getName() %></td>
					<td><%=tmp.getAddr() %></td>
				</tr>
			<%} %>
			</tbody>		
		</table>
	</div>
</body>
</html>


- 실제 DB에서 데이터를 읽어온다. tbody에서 for문을 돌면서 값 가져오기!

 

- 위와 같이 Oracle DB에 있는 데이터를 불러올 수 있다.

 


 

<insertform.jsp> 파일생성

- 회원정보 추가하기 폼

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/member/insertform.jsp</title>
</head>
<body>
	<div class="container">
		<h1>회원 추가 폼입니다.</h1>
		<form action="${pageContext.request.contextPath }/member/insert.jsp" method="post">
			<div>
				<label for="name">이름</label>
				<input type="text" name="name" id="name" />
			</div>
			<div>
				<label for="addr">주소</label>
				<input type="text" name="addr" id="addr" />
			</div>
			<button type="submit">추가</button>
		</form>
	</div>
</body>
</html>

- insert할 내용을 클라이언트로부터 입력받을 수 있는 폼 웹페이지 작성.

 

- 클라이언트에서 서버에 뭔가를 전달하기 위해서는 form 사용!
- input요소에 입력하고, submit으로 지정된 버튼을 누르면 전송한다.
- 이름과 주소가 요청하면서 전송된다.

 

 

<MemberDao>

- insert 메소드 추가

package test.member.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import javax.security.auth.message.callback.PrivateKeyCallback.Request;

import test.member.dto.MemberDto;
import test.util.DbcpBean;

public class MemberDao {
	//자신의 참조값을 저장할 static 필드(하나만 만들어놓고 사용할 예정이기 때문에)
	private static MemberDao dao;
	
	//외부에서 객체 생성하지 못하도록 생성자의 접근지정자를 private로 한다.
	private MemberDao() {}
	
	//자신의 참조값을 리턴하는 static 메소드를 제공한다.
	public static MemberDao getInstance() {
		//만일 최초 호출이라면 자신의 객체를 생성해서 static 필드에 담아둔다.
		if(dao==null) {
			dao=new MemberDao();
		}
		//필드에 있는 자신의 참조값을 리턴해 준다.
		return dao;
	}
	
	//전체 회원의 목록을 리턴하는 메소드
	public List<MemberDto> getList(){
		//회원 목록을 담을 객체 생성
		List<MemberDto> list=new ArrayList<>();
		
		//필요한 객체를 담을 지역변수를 미리 만들어둔다.
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//Connection Pool에서 Connection 객체를 하나 얻어온다.
			conn=new DbcpBean().getConn();
			//실행할 sql문의 뼈대 구성하기
			String sql="SELECT num, name, addr"
					+" FROM member"
					+" ORDER BY num ASC";
			//sql문의 ?에 바인딩할게 있으면 한다.
			
			pstmt=conn.prepareStatement(sql);
			//SELECT문을 수행하고 결과값을 받아온다.
			rs=pstmt.executeQuery();
			//반복문 돌면서 resultSet에서 필요한 값을 얻어낸다.
			while(rs.next()) {
				//커서가 위치한 곳에서 회원 한명의 정볼르 얻어내서 MemberDto 객체에 담은 후
				MemberDto dto=new MemberDto();
				dto.setNum(rs.getInt("num"));
				dto.setName(rs.getString("name"));
				dto.setAddr(rs.getString("addr"));
				//ArrayList 객체에 누적시킨다.
				list.add(dto);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null)rs.close();
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close(); //Connection Pool에 Connection 반납하기				
			} catch (Exception e2) {}						
		}	
		return list;		
	}
	
	//회원 한명의 정보를 DB에 저장하는 메소드
	public boolean insert(MemberDto dto){
		Connection conn=null;
		PreparedStatement pstmt=null;		
		int rowCount=0;
			try {
				conn=new DbcpBean().getConn();
				String sql="INSERT INTO member"
						+ " (num, name, addr)"
						+ " VALUES(member_seq.NEXTVAL, ?, ?)";		
				pstmt=conn.prepareStatement(sql);
				//?에 바인딩
				pstmt.setString(1, dto.getName());
				pstmt.setString(2, dto.getAddr());				
				//INSERT or UPDATE or DELETE문을 수행하고 수정,삭제,추가된 row의 개수 리턴
				rowCount=pstmt.executeUpdate();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if(pstmt!=null)pstmt.close();
					if(conn!=null)conn.close();
				} catch (Exception e2) {}
			}		
		
		if (rowCount>0) {
			return true;
		} else {
			return false;
		}
	}
}

 

- Select문으로 만들어둔 틀을 sql문 / ? 바인딩 / while문 부분 수정해서 데이터 저장하기

 

 

<insert.jsp>

<%@page import="test.member.dao.MemberDao"%>
<%@page import="test.member.dto.MemberDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	/*
		1. 폼 전송되는 회원의 이름과 주소를 읽어온다.
		2. MemberDao 에 MemberDto 를 전달하면 DB에 저장을 하고 작업의 성공여부를 리턴하는 insert() 메소드를 만든다.
		   회원번호는 시퀀스를 이용해서 들어가도록 한다.
		3. 위에서 추출한 회원의 이름과 주소를 MemberDto 객체에 담는다.
		4. MemberDao 객체를 이용해서 MemberDto 객체에 담긴 회원정보를 DB에 저장한다.
		5. 작업의 성공 여부를 클라이언트에게 응답한다.
	*/
	//폼 전송되는 회원의 이름과 주소를 읽어온다.
	request.setCharacterEncoding("utf-8");
	String name=request.getParameter("name");	
	String addr=request.getParameter("addr");
	
	//MemeberDto 객체에 회원의 이름과 주소를 담고	
	MemberDto dto=new MemberDto();
	dto.setName(name);
	dto.setAddr(addr);	
	
	//MemberDao객체를 이용해서 DB에 저장하고 작업의 성공여부를 리턴받는다.
	MemberDao dao=MemberDao.getInstance();
	boolean isSuccess=dao.insert(dto);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>member/insert.jsp</title>
</head>
<body>
	<div class="container">
		<%-- 작업의 성공 여부에 따라 다른 내용을 응답해준다. --%>
		<%if(isSuccess){%>
			<p>
				<strong><%=name %></strong>의 정보를 추가했습니다.
				<a href="${pageContext.request.contextPath }/member/list.jsp">확인</a>
			</p>	
		<%}else{%>
			<p>
				회원정보 추가 실패!
				<a href="${pageContext.request.contextPath }/member/insertform.jsp">다시 입력하러 가기</a>
			</p>
		<%}	%>
	</div>
</body>
</html>

- name, addr에서 받은 정보를 활용해서 DB에 데이터 저장하기

- jsp 파일의 request 변수를 활용해 입력받은 값을 읽어와 저장하고,

 boolean 값을 리턴해 클라이언트에게 성공 여부를 응답(반환)해준다.

 

1. getParameter를 사용해서 전송된 값을 읽어오기

 

public boolean insert(MemberDto dto){
    Connection conn=null;
    PreparedStatement pstmt=null;		
    int rowCount=0;
        try {
            conn=new DbcpBean().getConn();
            String sql="${cursor}";		
            pstmt=conn.prepareStatement(sql);
            //?에 바인딩

            //INSERT or UPDATE or DELETE문을 수행하고 수정,삭제,추가된 row의 개수 리턴
            rowCount=pstmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(pstmt!=null)pstmt.close();
                if(conn!=null)conn.close();
            } catch (Exception e2) {}
        }		

    if (rowCount>0) {
        return true;
    } else {
        return false;
    }
}

2. insert 메소드 만들어서 기본 틀 템플릿에 저장하기

 

3. MemberDto 객체에 저장

 

MemberDao dao=MemberDao.getInstance();

4. MemberDao객체를 이용해서 DB에 저장하고 작업의 성공여부를 리턴받는다.

 

5. if~else문으로 나누어 응답 내용을 다르게 한다.

- 빨간색: 공통적으로 응답되는 내용

- 초록색: 작업이 성공한 경우에 응답되는 내용

- 파란색: 작업이 실패한 경우에 응답되는 내용

 

- 성공한 경우 초록색 내용만 출력된다.

 

 

*실패했을 경우, '다시 입력하러 가기' 링크 <a>

<a href="insertform.jsp"> : 상대경로
<a href="${pageContext.request.contextPath }/member/insertform.jsp"> : 절대경로

- 절대경로는 틀리지 않는다! 특별한 일 없으면 절대경로로 하기

 

- 웹페이지, Oracle에서 위와 같이 회원정보가 추가된 것을 볼 수 있다.

 

- 서버의 DB, member 테이블 안에 있는 정보가 HTML마크업 안에 녹아 들어가 있다.
- 웹브라우저에서 보는 대부분의 정보는 DB에있는 정보를 가지고 클라이언트에게 응답하는 것.
 (html, css, javascript를 사용해서 보거나 사용하기 편리하게 응답된 것이다.)