반응형
01. PHONEBOOK 테이블
create table phonebook(phone varchar2(20) primary key, name varchar2(20), address varchar2(50));
INSERT INTO phonebook values('010-1111-1111','홍길동','서울시 송파구');
INSERT INTO phonebook values('010-2222-2222','임꺽정','서울시 강남구');
INSERT INTO phonebook values('010-3333-3333','전우치','부천시 원미구');
INSERT INTO phonebook values('010-4444-4444','일지매','수원시 팔달구');
INSERT INTO phonebook values('010-5555-5555','장길산','인천시 월미구');
commit;
select * from phonebook;
desc phonebook;

02. [비권장] 웹프로그램(서블릿) 내 JDBC 기능을 구현한 프로그램
- Connection 객체 이용함
- 생산성, 유지보수의 효율성 떨어짐
- 웹프로그램 내 JDBC 기능을 구현한 프로그램보다는 DAO 객체를 통해 DB 연동 메소드를 호출하여 만든 프로그램으로 작성하는 것을 권장
- oracle.jdbc.driver.OracleDriver
PhonebookOldServlet.java
package xyz.itwill.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//PHONEBOOK 테이블에 저장된 모든 회원정보를 검색하여 클라이언트에게 전달하는 서블릿 - JDBC 기능
// => JDBC 프로그램을 작성하기 위해 JDBC 관련 라이브러리 파일(ojdbc 라이브러리)을 다운로드 받아 프로젝트에 빌드 처리
@WebServlet("/old.itwill")
public class PhonebookOldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//2.
//JDBC 관련 객체를 저장하기 위한 참조변수 선언
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//3.
try {
//step1.
// => OracleDriver 클래스를 읽어 메모리에 저장
// => 내부적으로 OracleDriver 객체를 생성하여 DriverManager 클래스의 JDBC 드라이버로 등록
Class.forName("oracle.jdbc.driver.OracleDriver");
//step2.
// => DriverManager 클래스의 JDBC 드라이버로 DBMS 서버에 접속하여 Connection 객체를 반환받아 저장
String url="jdbc:oracle:thin:@localhost:1521:xe";
String user="scott";
String password="tiger";
con=DriverManager.getConnection(url, user, password);
//step3.
// => 접속된 DBMS 서버에 전달되어 실행될 SQL 명령이 저장된 PreparedStatement 객체를 Connection 객체로부터 반환받아 저장
String sql="select * from phonebook order by phone";
pstmt=con.prepareStatement(sql); //위에 인파라미타가 없으므로 그냥 작성
//step4.
// => PreparedStatement 객체에 저장된 SQL 명령을 전달하여 실행하고 결과를 반환받아 저장
rs=pstmt.executeQuery(); //rs:검색결과가 저장되어있음
//step5.
// => 반환받은 SQL 명령을 실행 결과를 이용하여 처리 작업 - SQL 명령의 실행 결과를 HTML 문서로 생성하여 클라이언트에게 전달
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset='UTF-8'>");
out.println("<title>Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>전화번호부</h1>");
//테이블로 출력함
out.println("<hr>");
out.println("<table border='1' cellspacing='0'>");
out.println("<tr>");
out.println("<th width='200'>전화번호</th>");
out.println("<th width='200'>이름</th>");
out.println("<th width='300'>주소</th>");
out.println("</tr>");
//ResultSet 객체에 저장된 모든 행의 컬럼값을 클라이언트에게 전달 - 반복 처리
while(rs.next()) {//ResultSet 커서를 다음행으로 이동하여 처리행이 존재할 경우 반복
out.println("<tr align='center'>");
//ResultSet 커서가 위치한 처리행의 컬럼값을 반환받아 클라이언트에게 전달
out.println("<td>"+rs.getString("phone")+"</td>");
out.println("<td>"+rs.getString("name")+"</td>");
out.println("<td>"+rs.getString("address")+"</td>");
out.println("</tr>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
//4.
} catch (ClassNotFoundException e) {
System.out.println("[에러]OracleDriver 클래스를 찾을 수 없습니다.");
} catch (SQLException e) {
System.out.println("[에러]JDBC 오류 = "+e.getMessage());
//5.
} finally {
//step6.
//사용했던 모든 JDBC 관련 객체 제거
try {
if(rs!=null) rs.close();
if(pstmt!=null) pstmt.close();
if(con!=null) con.close();
} catch (SQLException e) {}
}
}
}

03. [방법1] Apache Tomcat이 제공하는 DBCP 기능을 제공하는 서블릿
Tomcat 이 가진 ConectionPool 이용
- 기존에는 DBCP를 이용하기 위해 오라클 회사에서 제공하는 ucp.jar 빌드처리 해서 이용해함
- 이번에는 웹서버 Tomcat이 제공하는 아래의 클래스를 이용해 DBCP 만들 것임
DataSourceServlet.java
package xyz.itwill.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
//DBCP(DataBase Connection Pool) : DBMS 서버에 미리 접속하여 다수의 Connection 객체를 저장하여 제공하기 위한 기능의 클래스(객체)
// => JDBC 프로그램의 가독성이 높아지고 유지보수의 효율성 증가
// => DBCP 기능을 제공하는 클래스는 일반적으로 DataSource 인터페이스를 상속받아 작성
// => [DBCP 객체] = [DataSource 객체] 같은 뜻으로 보기
//Apache Tomcat 라이브러리에서 제공하는 DBCP 기능의 클래스를 사용하여 Connection 객체를 미리
//생성하고 생성된 Connection 객체를 제공받아 접속정보를 클라이언트에게 전달하는 서블릿
@WebServlet("/dbcp.itwill")
public class DataSourceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//2.
//BasicDataSource 객체(DataSource 객체 - DBCP) 생성
BasicDataSource dataSource = new BasicDataSource();
//3.
//BasicDataSource 객체에 저장될 다수의 Connection 객체 생성 관련 정보를 메소드를
//호출하여 객체 필드값 변경
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
dataSource.setUsername("scott");
dataSource.setPassword("tiger");
dataSource.setInitialSize(10); //최초 생성될 Connection 객체의 갯수 변경
dataSource.setMaxIdle(10); //대기 상태의 Connection 객체의 최대 갯수 변경
dataSource.setMaxTotal(10); //생성 가능한 최대 Connection 객체의 최대 갯수 변경
//4.
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset='UTF-8'>");
out.println("<title>Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>DBCP(DataBase Connection Pool)</h1>");
out.println("<hr>");
try {
//BasicDataSource 객체에 저장된 다수의 Connection 객체 중 하나를 반환받아 저장
Connection con=dataSource.getConnection();
out.println("<p>con= "+con+"</p>");
out.println("<hr>");
out.println("<h3>Connection 객체 제공 후</h3>");
out.println("<p>Active Connection Number = "+dataSource.getNumActive()+"</p>");
out.println("<p>Idle Connection Number = "+dataSource.getNumIdle()+"</p>");
con.close();
out.println("<hr>");
out.println("<h3>Connection 객체 제거 후</h3>");
out.println("<p>Active Connection Number = "+dataSource.getNumActive()+"</p>");
out.println("<p>Idle Connection Number = "+dataSource.getNumIdle()+"</p>");
dataSource.close();
} catch (SQLException e) {
e.printStackTrace();
}
out.println("</body>");
out.println("</html>");
}
}

04. [방법2-권장] WAS에 미리 등록해 Apache Tomcat이 제공하는 DBCP 기능을 제공하는 서블릿
WAS에 등록시키면?
- 앞서 우리가 직접 객체를 만들었지만, WAS(context.xml 폴더)에 등록시키고 WAS를 통해 객체를 만들 수 있도록 하면 WAS가 종료될 때 같이 종료됨 - 웹컨테이너역할
- JNDI 기술 - 자바 자원에 이름을 부여해 DAO 클래스를 만들어서 사용하는 것
META-INF & WEB-INF
- WAS가 start 시 웹자원으로 바뀌긴 하지만, 클라이언트가 접근할 수 없는 은닉화된 폴더
META-INF
- WAS 가 start할 때 읽어들일 정보를 저장할 파일 (웹과 관련없는 파일들 저장)
- 메타파일을 만드는 것은 필수는 아니지만, 나중에 필요한 정보를 제공받아 쓸 수 있기 때문에 유용
WEB-INF
- 웹프로그램이 쓰려고 만든 파일 (웹과 관련된 파일 저장)
webapp > META-INF > context.xml - JNDI기술
- WAS 프로그램 객체 생성 관련 정보를 제공하기 위한 파일
- WAS 프로그램 실행 시 자동으로 파일을 읽어 필요한 정보를 저장
webapp > META-INF > context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Context : context.xml 파일의 최상위 엘리먼트 -->
<Context>
<!-- Resource : 객체 생성 관련 정보를 속성과 속성값으로 제공하는 엘리먼트 -->
<Resource name="jdbc/oracle" auth="Container" type="javax.sql.DataSource"
factory="org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:xe" username="scott" password="tiger"
initialSize="10" maxIdle="10" maxTotal="15"/>
</Context>
<!-- WAS에 등록된 정보 : factory클래스로 객체를 만들며, 객체에 필요한 속성값은 아래처럼 설정했어요 -->
<!-- [jdbc/oracle]라는 이름으로 객체가 반환될 것이에요 -->
<!--
1.[name]: Resource 엘리먼트를 구분하기 위한 식별자를 속성값으로 설정(중복X), 객체 생성에 필요한 정보를 WAS 프로그램으로부터 제공하기 위한 이름
2.[auth]: 객체를 생성을 위한 정보를 제공한 사용자를 속성값으로 설정, 아무렇게나 작명해도 됨
3.[type]: 객체를 생성하여 반환받기 위한 자료형 (클래스 또는 인터페이스를 속성값으로 설정)
4.[factory]: 객체를 생성하기 위한 Factory 클래스를 속성값으로 설정, Factory 클래스에 필요한 값을 속성명(필드명)과 속성값(필드값)을 사용하여 제공
-->
JndiServlet.java (JNDI) → (Java Naming Directory Interface)
package xyz.itwill.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
//JNDI(Java Naming Directory Interface) :
//WAS 프로그램에 객체 생성에 필요한 정보를 이름(식별자)으로 구분하여 저장하고, 필요한 경우 객체 생성 정보를 제공받아 객체를 생성하여 사용하는 기능
//=> 객체 생성에 필요한 정보를 제공하는 파일 필요 - src/main/webapp/META-INF/context.xml
//WAS 프로그램에 등록된 자원(Resource)을 이용하여 DataSource 객체를 생성하고 DataSource 객체에 저장된 다수의 Connection 객체 중 하나를 제공받아 클라이언트에게 접속정보를 전달하는 서블릿
//WAS에 미리 등록되어 사용하는 것이므로 properties 파일 사용보다 가독성이 훨씬 더 좋음
@WebServlet("/jndi.itwill")
public class JndiServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
try {
//InitialContext 객체 : WAS 프로그램에 등록된 자원을 이용하여 객체를 생성하여 제공하기 위한 객체
//=> NamingException(이름에 대한 자원 정보가 없는 경우 발생되는 예외) 발생 - 예외처리필수
InitialContext ic = new InitialContext();
//InitialContext.lookup(String name) : 매개변수로 전달받은 이름의 자원을 WAS 프로그램에게 제공받아 객체를 생성하여 반환하는 메소드
//=> Object 타입의 객체를 반환하므로 반드시 명시적 객체 형변환하여 사용
DataSource dataSource = (DataSource)ic.lookup("java:comp/env/jdbc/oracle");
//java : 자바 자원으로 등록되있다
//comp : 객체관련생성 정보가 담긴 폴더
//env : 환경설정관련 정보가 담긴 폴더
Connection con = dataSource.getConnection();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset='UTF-8'>");
out.println("<title>Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>DBCP(JNDI)</h1>");
out.println("<hr>");
out.println("<p>con = "+con+"</p>");
out.println("</body>");
out.println("</html>");
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

05. [권장] DB 연동 메소드를 호출하는 DAO객체로 JDBC기능을 구현한 프로그램
[DTO] PhonebookDTO.java
package xyz.itwill.dto;
//DTO (Data Transfer Object) 클래스 : 테이블의 행정보를 저장하여 전달하기 위한 클래스 - VO 클래스(광범위)
/*
이름 널? 유형
------- -------- ------------
PHONE NOT NULL VARCHAR2(20)
NAME VARCHAR2(20)
ADDRESS VARCHAR2(50)
*/
//PHONEBOOK 테이블의 회원정보를 저장하기 위한 클래스
public class PhonebookDTO {
private String phone;
private String name;
private String address;
//기본생성자 : [Ctrl]+[Space] >> Constructor 선택
public PhonebookDTO() { }
//생성자 : [Alt]+[Shift]+[S] >> [O] >> 필드선택 >> Generate
public PhonebookDTO(String phone, String name, String address) {
super();
this.phone = phone;
this.name = name;
this.address = address;
}
//Getter & Setter : [Alt]+[Shift]+[S] >> [R] >> 필드선택 >> Generate
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getAddress() {return address;}
public void setAddress(String address) {this.address = address;}
}
[부모 DAO] jdbcDAO.java
package xyz.itwill.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
//JDBC 기능을 제공하는 DAO 클래스가 상속받기 위해 작성된 부모 클래스
// => 객체 생성이 목적이 아닌 상속으로 목적으로 작성된 클래스 - 추상클래스로 작성하는 것을 권장
// => WAS 프로그램에 등록된 자원을 얻어와 DataSource 객체를 반환받아 저장 - 정적영역을 이용해 1번만 실행
// => DataSource 객체로부터 Connection 객체를 제공받아 반환하는 메소드
// => 매개변수로 JDBC 관련 객체를 제공받아 제거하는 메소드
public abstract class jdbcDAO {
//1.
//WAS 프로그램에 등록된 자원을 얻어와 DataSource 객체를 반환받아 저장 - 정적영역을 이용해 1번만 실행
private static DataSource dataSource; //Connection이 미리 10개 생성됨
static { //메모리가 로딩되면 자동으로 아래의 명령 실행되어 객체 만들어짐
try {
dataSource = (DataSource)new InitialContext().lookup("java:comp/env/jdbc/oracle");
}catch (NamingException e) {
e.printStackTrace();
}
}
//2.
//DataSource 객체로부터 Connection 객체를 제공받아 반환하는 메소드
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//3.
//매개변수로 JDBC 관련 객체를 제공받아 제거하는 메소드
public void close(Connection con) {
try {
if(con!=null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close(Connection con, PreparedStatement pstmt) {
try {
if(con!=null) con.close();
if(pstmt!=null) pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close(Connection con, PreparedStatement pstmt, ResultSet rs) {
try {
if(con!=null) con.close();
if(pstmt!=null) pstmt.close();
if(rs!=null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
[자식 DAO] PhonebookDAO.java
package xyz.itwill.dao;
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 xyz.itwill.dto.PhonebookDTO;
//DAO(Data Access Object) 클래스 : 테이블에 행 삽입,변경,삭제,검색 명령을 전달하여 실행하고 처리 결과를 Java 객체(값)으로 변환하는 기능을 제공하는 클래스
// => 싱글톤 클래스(프로그램에 객체를 하나만 생성하여 제공하는 클래스)로 작성하는 것을 권장
//PHONEBOOK 테이블에 회원정보를 삽입,변경,삭제,검색하는 기능을 제공하는 클래스
public class PhonebookDAO extends jdbcDAO {
private static PhonebookDAO _dao;
private PhonebookDAO() { //싱글톤 클래스의 핵심 - 은닉화된 생성자
// TODO Auto-generated constructor stub
}
static {
_dao = new PhonebookDAO();
}
public static PhonebookDAO getDAO() {
return _dao;
}
//PHONEBOOK 테이블에 저장된 모든 회원정보를 검색하여 반환하는 메소드
public List<PhonebookDTO> selectPhonebookList() {
Connection con = null;
PreparedStatement pstmt=null;
ResultSet rs=null;
List<PhonebookDTO> phonebookList = new ArrayList<PhonebookDTO>();
try {
con = getConnection(); //미리만들어진 Connection 객체 1개 제공받음
String sql = "select * from phonebook order by phone";
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()) {
PhonebookDTO phonebook = new PhonebookDTO();
//resultSet 커서가 가진 처리행을 DTO객체의 필드에 저장하고
phonebook.setPhone(rs.getString("phone"));
phonebook.setName(rs.getString("name"));
phonebook.setAddress(rs.getString("address"));
//list의 요소로 하나씩 추가하기
phonebookList.add(phonebook);
}
} catch (SQLException e) {
System.out.println("[에러] selectPhonebookList() 메소드의 SQL 오류 = " + e.getMessage());
} finally {
close(con, pstmt, rs);
}
return phonebookList;
}
}
[Servlet] PhonebookNewServlet.java
package xyz.itwill.servlet;
import java.io.IOException;
import java.io.PrintWriter;
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;
import xyz.itwill.dao.PhonebookDAO;
import xyz.itwill.dto.PhonebookDTO;
//PHONEBOOK 테이블에 저장된 모든 회원정보를 검색하여 클라이언트에게 전달하는 서블릿 - DAO
@WebServlet("/new.itwill")
public class PhonebookNewServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//vs PHONEBOOK 테이블에 저장된 모든 회원정보를 검색하여 반환하는 DAO 클래스의 메소드 호출
List<PhonebookDTO> phonebookList = PhonebookDAO.getDAO().selectPhonebookList(); //모든 행을 검색해서 결과를 List객체로 반환
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset='UTF-8'>");
out.println("<title>Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>전화번호부</h1>");
out.println("<table border='1' cellspacing='0'>");
out.println("<tr>");
out.println("<th width='200'>전화번호</th>");
out.println("<th width='200'>이름</th>");
out.println("<th width='300'>주소</th>");
out.println("</tr>");
//List 객체에 저장된 요소를 반복적으로 제공받아 클라이언트에게 전달 - 반복처리
for(PhonebookDTO phonebook:phonebookList) {
out.println("<tr align='center'>");
out.println("<td>"+phonebook.getPhone()+"</td>");
out.println("<td>"+phonebook.getName()+"</td>");
out.println("<td>"+phonebook.getAddress()+"</td>");
out.println("</tr>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
}
}
반응형
'servlet' 카테고리의 다른 글
[servlet] 9. 방명록 프로그램 (0) | 2024.05.26 |
---|---|
[servlet] 7. 바인딩된 세션 전달 프로그램 (session) (0) | 2024.05.25 |
[servlet] 6. 쿠키 생성, 전달, 제거 프로그램 (cookie) (0) | 2024.05.25 |
[servlet] 5. 파일업로드 프로그램 (multipart/form-data) (0) | 2024.05.24 |
[servlet] 4. 회원가입 프로그램 (form) (0) | 2024.05.24 |