본문 바로가기

학원/복기

[Servlet] Servelet과 JDBC 연동 (DAO 사용한 예제와 JNDI)

STUDENT 테이블에 저장된 모든 학생정보를 검색하여 클라이언트에게 전달하여 응답하는 서블릿 - JDBC를 이용해 연동 (비권장)


STUDENT 테이블에 저장된 모든 학생정보를 검색하기 위해서는 JDBC 프로그램을 만들어주어야 한다.

JDBC 프로그램을 작성하기 위해서는 JDBC 관련 라이브러리 파일(ojdbc 라이브러리)을 프로젝트에 빌드 처리 해야한다.

 

 

[전체 소스코드]

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;

@WebServlet("/old.itwill")
public class StudentDisplayOldServlet 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();
		
		//JDBC 관련 객체를 저장하기 위한 변수 선언
		Connection con=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//1.OracleDriver 클래스를 읽어 메모리에 저장
			// => OracleDriver 객체가 생성되어 DriverManager 클래스의 JDBC Driver로 등록
			Class.forName("oracle.jdbc.driver.OracleDriver");
			
			//2.DriverManager 클래스에 등록된 JDBC 드라이버를 이용하여 DBMS 서버에 접속해
			//접속정보가 저장된 Connection 객체를 반환받아 저장
			String url="jdbc:oracle:thin:@localhost:1521:xe";
			String username="scott";
			String password="tiger";
			con=DriverManager.getConnection(url, username, password);
			
			//3.Connection 객체로부터 SQL 명령이 저장된 PreparedStatement 객체를 반환받아 저장
			String sql="select * from student order by no";
			pstmt=con.prepareStatement(sql);
			
			//4.PreparedStatement 객체에 저장된 SQL 명령을 DBMS 서버에 전달하여 실행한 후 실행결과를 반환받아 저장
			rs=pstmt.executeQuery();
			
			//5.반환받은 실행결과를 사용자에게 제공
			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='100'>학번</th>");
			out.println("<th width='150'>이름</th>");
			out.println("<th width='200'>전화번호</th>");
			out.println("<th width='300'>주소</th>");
			out.println("<th width='250'>생년월일</th>");
			out.println("</tr>");
			//ResultSet 객체에 저장된 모든 행의 컬럼값을 반환받아 클라이언트에 전달 - 반복처리
			while(rs.next()) {
				out.println("<tr>");
				out.println("<td align='center'>"+rs.getInt("no")+"</td>");
				out.println("<td align='center'>"+rs.getString("name")+"</td>");
				out.println("<td align='center'>"+rs.getString("phone")+"</td>");
				out.println("<td align='center'>"+rs.getString("address")+"</td>");
				out.println("<td align='center'>"+rs.getString("birthday").substring(0, 10)+"</td>");
				out.println("</tr>");
			}
			out.println("</body>");
			out.println("</html>");
		} catch (ClassNotFoundException e) {
			System.out.println("[에러]OracleDriver 클래스를 찾을 수 없습니다.");
		} catch (SQLException e) {
			System.out.println("[에러]JDBC 관련 오류 = "+e.getMessage());
		} finally {
			//6.JDBC 관련 객체 제거
			try {
				if(rs!=null) rs.close();
				if(pstmt!=null) pstmt.close();
				if(con!=null) con.close();
			} catch (SQLException e) {}
		}
		
	}
}

 

 

하지만 이처럼 프로그램을 작성하는 것은 만들기 어렵고 유지보수도 어렵기 때문에 권장하는 방법은 아니다.

따라서 DAO를 이용해 서블릿을 만들어 주는 것을 권장한다.


DAO 이용한 예제 

 


 

STUDENT 테이블에 저장된 모든 학생정보를 검색하여 클라이언트에게 전달하여 응답하는 서블릿 생성하기 

package xyz.itwill.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/new.itwill")
public class StudentDisplayNewServlet 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();
        
            //코드 선언
            //코드 선언 
            // ...
	}

}

 


1. DTO 클래스 선언 

DTO 클래스 생성

 

DTO(Data Transfer Object) 클래스

DTO 클래스는 테이블의 행정보를 표현하여 전달하기 위한 클래스이다. (VO 클래스) 

 

student 테이블 스키마(테이블의 컬럼명과 컬럼들의 제약조건 정보) 확인


 

STUDENT 테이블의 학생정보를 저장하여 전달하기 위한 StudentDTO 클래스 선언 

package xyz.itwill.dto;

public class StudentDTO {
	private int no;
	private String name;
	private String phone;
	private String address;
	private String birthday;
	
	//생성자 생성 
	public StudentDTO() {
		// TODO Auto-generated constructor stub
	}
	
	public StudentDTO(int no, String name, String phone, String address, String birthday) {
		super();
		this.no = no;
		this.name = name;
		this.phone = phone;
		this.address = address;
		this.birthday = birthday;
	}
	
	//Setter Getter 메소드 생성 
	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getName() {
		return name;
	}

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

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getBirthday() {
		return birthday;
	}

	public void setBirthday(String birthday) {
		this.birthday = birthday;
	}
}

2. DBCP 객체 생성 

Apache 그룹에서 제공하는 tomcat-dbcp 라이브러리의 클래스를 이용하여 DBCP 객체를 생성해 미리 생성된 Connection 객체를 제공받아 Connection 객체의 정보를 클라이언트에게 전달하여 응답하는 서블릿 생성

 

DBCP(DataBase Connection Pool) 객체는 다수의 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 {
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out=response.getWriter();
        
                //코드 선언
                //코드 선언
                // ...
	}
}

 

BasicDataSource 객체(DataSource 객체)를 생성한다.

BasicDataSource dataSource = new BasicDataSource();

 

BasicDataSource 객체(DataSource 객체)에 저장될 다수의 Connection 객체를 생성하기 위한 정보를 메소드를 호출하여 변경 처리한다.  - DataSource 객체의 필드값 변경 

//필수
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDirver");
dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
dataSource.setUsername("scott");
dataSource.setPassword("tiger");
//선택
dataSource.setInitialSize(10);//최초 생성될 Connection 객체의 갯수 변경(생략하면 20개로 설정된다) 
dataSource.setMaxIdle(10);//대기상태의 Connection 객체의 최대 갯수 변경 
dataSource.setMaxTotal(15);//생성 가능한 Connection 객체의 최대 갯수
//...
//...

 

응답파일 생성 

 

DataSource.getConnection() : DataSource 객체에 저장된 다수의 Connection 객체중 하나를 제공받아 반환하는 메소드 

DataSource.getNumIdle() : DataSource 객체에 저장된 대기상태의 Connection 객체의 갯수를 반환하는 메소드 

DataSource.getNumActive() : DataSource 객체에 저장된 다수의 Connection 객체 중 사용중인 Connection 객체의 갯수를 반환하는 메소드

 

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 {
	Connection con=dataSource.getConnection();
	out.println("<p>con = "+con+"</p>");
	out.println("<hr>");
	out.println("<h3>Connection 객체 제공 후</h3>");
	out.println("<p>Idle Connection Number = "+dataSource.getNumIdle()+"</p>");
	out.println("<p>Active Connection Number = "+dataSource.getNumActive()+"</p>");
	out.println("<hr>");
	con.close();//Connection 객체 제거 - DataSource 객체에서 Connection 객체를 대기상태로 변경
	out.println("<h3>Connection 객체 제거 후</h3>");
	out.println("<p>Idle Connection Number = "+dataSource.getNumIdle()+"</p>");
	out.println("<p>Active Connection Number = "+dataSource.getNumActive()+"</p>");
			
    dataSource.close();
} catch (SQLException e) {
	e.printStackTrace();
}
out.println("</body>");
out.println("</html>");

이 방법 외에, JNDI을  이용해서 DBCP객체를 더 쉽게 만들 수 있다.


 

JNDI(Java Naming Directory Interface)

JNDI는 WAS 프로그램에 의해 관리되는 객체를 미리 생성하여 저장하고 웹프로그램에서 필요한 경우 WAS 프로그램에 등록된 객체의 이름을 이용하여 객체를 제공받아 사용하기 위한 기능이다.

 

JNDI를 통해 DBCP 객체 생성하기 

  1. 웹 애플리케이션의 WEB-INF 디렉토리 또는 META-INF 디렉토리에 context.xml 파일을 생성한다. 해당 파일은 Tomcat과 같은 서블릿 컨테이너에서 사용하는 설정 파일이다.
  2. context.xml 파일에 DBCP의 설정 정보를 작성한다.  예를 들어, 데이터베이스의 JDBC 드라이버 클래스, 연결 URL, 사용자 이름, 비밀번호 등을 설정합한다. 또한, DBCP의 풀 크기, 유휴 커넥션 유지 시간 등과 같은 세부 설정도 지정할 수 있다. 
  3. 웹 애플리케이션의 코드에서 JNDI를 사용하여 DBCP 객체를 검색한다. 일반적으로 javax.naming.InitialContext 클래스를 사용하여 JNDI 컨텍스트를 얻고, lookup() 메서드를 사용하여 DBCP 객체를 검색한다.
  4. 검색한 DBCP 객체를 사용하여 데이터베이스 커넥션을 가져온다. DBCP는 설정된 풀 크기에 따라 커넥션을 관리하며, 애플리케이션이 데이터베이스에 접근할 때마다 DBCP에서 사용 가능한 커넥션을 제공한다. 

 

1) META-INF 디렉토리에 context.xml 파일 생성 

 

context.xml 파일 생성

 

이름은 반드시 context 여야 한다.

 

context.xml은 WAS 프로그램에 의해 관리될 객체 관련 정보를 제공하기 위한 파일이다.

context.xml은 WAS 프로그램이 실행(Start)될때 자동으로 파일을 읽어 객체를 생성하여 WAS 프로그램에 의해 관리된다.

웹프로그램에서는 WAS 프로그램에게 필요한 객체를 제공받아 사용할 수 있다. 이 때 사용하는 것이 lookup()메소드이다. 

 

 

2) context.xml 파일에 DBCP의 설정 정보를 작성

 

Context : context.xml 파일의 최상위 엘리먼트 - Resource 엘리먼트를 자식 엘리먼트로 여러개 설정할 수 있다.

Resource : WAS 프로그램에 의해 관리될 객체 관련 정보를 제공하는 엘리먼트 → 객체 관련 정보를 엘리먼트의 속성과 속성값으로 제공한다.

<Context>
	<Resource></Resource>
</Context>

 

객체의 필드값 관련 정보를 속성(필드명)과 속성값(필드값)을 이용하여 설정한다. (필드값 변경) 

 

name 속성 : Resource 엘리먼트를 구분하기 위한 식별자를 속성값으로 사용한다. name은 WAS 프로그램에게 객체를 제공받기 위한 이름으로 사용한다.

author 속성 : 객체를 생성하기 위한 사용자 이름을 속성값으로 설정한다.

factory 속성 : 객체를 생성하기 위한 Factory 클래스를 속성값으로 설정한다. factory 속성 대신 class 속성을 사용하여 객체를 생성하는 것이 가능하다. 

type 속성 : 객체의 자료형을 속성값으로 설정해 객체를 반환받기 위한 자료형(클래스 or 인터페이스)을 알려줄 수 있도록 한다.

<Context>
	<Resource name="jdbc/oracle" auth="Container"
		factory="org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"	
		type="javax.sql.DataSource" driverClassName="oracle.jdbc.driver.OracleDriver"
		url="jdbc:oracle:thin:@localhost:1521:xe" username="scott" password="tiger"
		initialSize="10" maxIdle="10" maxTotal="15"/>
</Context>

 

 

3) 웹 애플리케이션의 코드에서 JNDI를 사용하여 DBCP 객체를 검색

 

WAS 프로그램에 등록된 DataSource 객체를 제공받아 DataSource 객체에 저장된 다수의 Connection 객체 중 하나를 제공받아 Connection 객체의 정보를 클라이언트에게 전달하여 응답하는 서블릿 생성

 

InitialContext 객체 : WAS 프로그램에 등록된 객체 관련 JNDI 서비스를 제공하는 객체

→ JNDI 서비스 : WAS 프로그램에 의해 관리되는 객체를 제공하기 위한 서비스

 

InitialContext.lookup(String name) : 매개변수로 전달받은 이름의 객체를 반환하는 메소드 

→ 검색된 객체는 Object 객체로 반환되므로 반드시 명시적 객체 형변환을 하여 사용해야 한다.

→ 매개변수로 전달받은 이름의 객체가 없는 경우 NamingException이 발생한다. - 반드시 예외처리 해주어야 한다.

 

package xyz.itwill.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
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;


@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 {
        		//JNDI를 사용하여 DBCP 객체를 검색
			DataSource dataSource=(DataSource)new InitialContext().lookup("java:comp/env/jdbc/oracle");
			
			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 (NamingException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
}

 

 

3. JdbcDAO 클래스 생성

JDBC 기능을 제공하는 DAO 클래스가 상속받아 사용하기 위해 작성된 부모 클래스 

→ WAS 프로그램에 의해 관리되는 DataSource 객체를 제공받아 필드에 저장 (정적 영역에 작성하여 한번만 실행할 것임)

→ DataSource 객체로부터 Connection 객체를 제공받아 반환하는 메소드 생성

매개변수로 전달받은 JDBC 관련 객체를 제거하는 메소드 생성 

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 클래스가 상속받아 사용하기 위해 작성된 부모클래스 - 추상클래스
public abstract class JdbcDAO {//상속만을 목적으로 작성된 클래스
	private static DataSource dataSource;
	
	static {
		try {
			dataSource=(DataSource)new InitialContext().lookup("java:comp/env/jdbc/oracle");
		} catch (NamingException e) {
			e.printStackTrace();
		}	
	}
		
	public Connection getConnection() throws SQLException {
		return dataSource.getConnection();
	}
	
	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(pstmt!=null) pstmt.close();
			if(con!=null) con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}	
	}
	
	public void close(Connection con, PreparedStatement pstmt, ResultSet rs) {
		try {
			if(rs!=null) rs.close();
			if(pstmt!=null) pstmt.close();
			if(con!=null) con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}	
	}
}

4. DAO 클래스 생성

DAO(Data Access Object) 클래스

DAO 클래스는 테이블에 행 삽입, 삭제, 변경, 검색하는 SQL 명령을 전달하여 실행하고 실행결과를 Java 객체(값)로 매핑하여 반환하는 기능을 제공하는 클래스이다. 

→  싱글톤 클래스로 작성하는 것을 권장한다. (싱글톤 클래스는 프로그램에 객체를 하나만 생성하여 제공하는 클래스이다.)

 

STUDENT 테이블에 학생정보를 삽입, 삭제, 변경, 검색하는 기능을 제공하는  StudentDAO 클래스 생성 

DAO 클래스 생성

StudentDAO 클래스가 JdbcDAO 클래스를 상속받는다.

//싱글톤 패턴을 사용하여 StudentDAO 클래스의 인스턴스를 생성
public class StudentDAO extends JdbcDAO {
	private static StudentDAO _dao;
	
	//생성자를 private으로 선언 
	public StudentDAO() {
		
	}
	
	static {
		_dao=new StudentDAO();
	}
	
	public static StudentDAO getDAO() {
		return _dao;
	}
    
        //코드 선언
        //코드 선언
        // ...
}

 

 

STUDENT 테이블에 저장된 모든 학생정보를 검색하여 반환하는 메소드 선언 

public List<StudentDTO> selectStudentList() {
	Connection con=null;
	PreparedStatement pstmt=null;
	ResultSet rs=null;
	List<StudentDTO> studentList=new ArrayList<>();
	try {
		con=getConnection();//10개의 커넥션 중 하나를 반환받아 저장
			
		String sql="select * from student order by no";
		pstmt=con.prepareStatement(sql);
			
		rs=pstmt.executeQuery();
			
		while(rs.next()) {
			//ResultSet 커서 위치의 행을 DTO 객체로 표현 
			// => ResultSet 커서 위치의 행에 대한 컬럼값은 DTO 객체의 필드에 매핑하여 저장
			StudentDTO student=new StudentDTO();
			student.setNo(rs.getInt("no"));
			student.setName(rs.getString("name"));
			student.setPhone(rs.getString("phone"));
			student.setAddress(rs.getString("address"));
			student.setBirthday(rs.getString("birthday"));
				
			//List 객체에 DTO 객체를 요소로 추가 
			studentList.add(student);
		}
	} catch (SQLException e) {
		System.out.println("[에러]selectStudentList 메소드의 SQL 오류 = "+e.getMessage());
	} finally {
		close(con, pstmt, rs);
	}
	return studentList; 
}

5. StudentDisplayNewServlet 구현

package xyz.itwill.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//STUDENT 테이블에 저장된 모든 학생정보를 검색하여 클라이언트에게 전달하여 응답하는 서블릿 - DAO 
@WebServlet("/new.itwill")
public class StudentDisplayNewServlet 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();
        
        //코드작성
        //코드작성
        //...
	}

}

 

STUDENT 테이블에 저장된 모든 학생정보를 검색하여 List 객체로 반환하는 DAO 클래스의 메소드 호출 

List<StudentDTO> studentList=StudentDAO.getDAO().selectStudentList();

 

반환받은 실행결과를 사용자에게 제공

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='100'>학번</th>");
out.println("<th width='150'>이름</th>");
out.println("<th width='200'>전화번호</th>");
out.println("<th width='300'>주소</th>");
out.println("<th width='250'>생년월일</th>");
out.println("</tr>");
//List 객체에 저장된 요소(StudentDTO 객체)를 차례대로 제공받아 변수에 저장하는 반복문
for(StudentDTO student : studentList) {
	out.println("<tr>");
	out.println("<td align='center'>"+student.getNo()+"</td>");
	out.println("<td align='center'>"+student.getName()+"</td>");
	out.println("<td align='center'>"+student.getPhone()+"</td>");
	out.println("<td align='center'>"+student.getAddress()+"</td>");
	out.println("<td align='center'>"+student.getBirthday().substring(0, 10)+"</td>");
	out.println("</tr>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");

 

이처럼 DAO를 만들어서 DAO 메소드로 JDBC를 구현하면  유지보수가 쉽다는 장점이 있다.

'학원 > 복기' 카테고리의 다른 글

[JSP] JSP란?  (0) 2023.06.20
[Servlet] 방명록 프로그램 예제 (삽입,변경,삭제,검색)  (0) 2023.06.20
[Servlet] 세션(Session)  (0) 2023.06.18
[Servlet] 쿠키(Cookie)  (0) 2023.06.18
[Servlet] 파일 업로드 서블릿  (0) 2023.06.18