데이터베이스 연동

아래 나오는 모든 예제는 이클립스를 사용하여 ROOT 애플리케이션의 도큐먼트 베이스에 작성한다.
명함관리 웹 애플리케이션의 3번째 테스트를 참고하여 이클립스에 ROOT 애플리케이션을 위한 작업환경을 구축한다.

ROOT 애플리케이션의 빌드 패스에 서블릿 API 추가

이클립스에서 이전에 실습했던 GetEmpServlet 서블릿을 연다.
많은 컴파일 에러를 발생한다.
에러 원인은 ROOT 애플리케이션의 빌드 패스에 서블릿 API를 추가하지 않았기 때문이다.
아래 화면처럼 Libraries 탭을 선택하고, Add External JARs..버튼을 클릭하여 CATALINA_HOME/lib에서 서블릿 API를 찾아 추가한다.
서블릿 API를 ROOT애플리케이션의 빌드패스에 추가

첫 번째 JSP

getEmp1.jsp 파일을 ROOT 애플리케이션의 도큐먼트 베이스에 아래와 같이 만든다.
(이 JSP는 GetEmpServlet 서블릿의 JSP 버전이다)

getEmp1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>첫 번째 예제</title>
</head>
<body>
<%
String DB_URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
String DB_USER = "scott";
String DB_PASSWORD = "tiger";

try {
	Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;

String sql = "select * from emp";

try {
	con = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
	stmt = con.prepareStatement(sql);
	rs = stmt.executeQuery();

	while (rs.next()) {
		String empno = rs.getString(1);
		String ename = rs.getString(2);
		String job = rs.getString(3);
		String mgr = rs.getString(4);
		String hiredate = rs.getString(5);
		String sal = rs.getString(6);
		String comm = rs.getString(7);
		String depno = rs.getString(8);
		
		out.println( empno + " : " + ename + " : " + job + " : " + mgr + 
		" : " + hiredate + " : " + sal + " : " + comm + " : " + depno + "<br />" );
	}

} catch (SQLException e) {
	System.out.println("Error Source : getEmp1.jsp : SQLException");
	System.out.println("SQLState : " + e.getSQLState());
	System.out.println("Message : " + e.getMessage());
	System.out.println("Oracle Error Code : " + e.getErrorCode());
	System.out.println("sql : " + sql);
} finally {
	if (rs != null) {
		try {
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	if (stmt != null) {
		try {
			stmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	if (con != null) {
		try {
			con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
%>
</body>
</html>

JSP 파일이므로 톰캣을 재실행할 필요는 없다.
http://localhost:8080/getEmp1.jsp를 방문하여 테스트한다.

두 번째 JSP 예제

사용자 정의 커넥션 풀링 사용하기

JDBC에서 커넥션 객체를 획득하는 데에 시간이 많이 걸린다.
이에 대한 해결책으로 커넥션 풀링이 있다.
커넥션 풀링은 커넥션을 미리 여러 개 만들어 벡터와 같은 컬렉션에 저장해 두고 필요할 때마다 꺼내 쓰겠다는 아이디어다.

getEmp1.jsp가 커넥션 풀을 통해서 커넥션을 얻도록 수정해 보자.
우리는 이미 서블릿 장 실습에서 ROOT 애플리케이션에 커넥션 풀링을 추가했다.
(다음 주소 참조: 사용자 정의 커넥션 풀 사용하기)
getEmp1.jsp를 열고 Save As...메뉴를 사용하여 getEmp2.jsp를 생성한 후 아래와 같이 수정한다.

getEmp2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<%@ page import="net.java_school.db.dbpool.*" %>
<%@ page import="net.java_school.util.*" %>
<jsp:useBean id="dbmgr" class="net.java_school.db.dbpool.OracleConnectionManager" scope="application" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>두 번째 JSP 예제</title>
</head>
<body>
<%
Connection con = null;
Statement stmt = null;
ResultSet rs = null;

String sql = "select * from emp";

try {
	con = dbmgr.getConnection();
	stmt = con.createStatement();
	rs = stmt.executeQuery(sql);

	while (rs.next()) {
		String empno = rs.getString(1);
		String ename = rs.getString(2);
		String job = rs.getString(3);
		String mgr = rs.getString(4);
		String hiredate = rs.getString(5);
		String sal = rs.getString(6);
		String comm = rs.getString(7);
		String depno = rs.getString(8);
		
		out.println( empno + " : " + ename + " : " + job + " : " + mgr + 
		" : " + hiredate + " : " + sal + " : " + comm + " : " + depno + "<br />" );
	}

} catch (SQLException e) {
	System.out.println("Error Source : getEmp2.jsp : SQLException");
	System.out.println("SQLState : " + e.getSQLState());
	System.out.println("Message : " + e.getMessage());
	System.out.println("Oracle Error Code : " + e.getErrorCode());
	System.out.println("sql : " + sql);
} finally {
	if (rs != null) {
		try {
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	if (stmt != null) {
		try {
			stmt.close();
		} catch (SQLException e) {

			e.printStackTrace();
		}
	}
	if (con != null) {
		dbmgr.freeConnection(con);
	}
}
%>
</body>
</html>

두 번째 JSP는 jsp:useBean 액션을 사용하여 OracleConnectionManager 객체 레퍼런스를 획득한다.
두 번째 JSP는 커넥션이 필요할 때마다 OracleConnectionManager의 getConnection() 메소드를 호출하면 커넥션을 획득할 수 있다.
이 사용자 정의 커넥션 풀을 사용할 때는, 커넥션을 사용한 후 close() 메소드를 사용하지 말고 OracleConnectionManager의 freeConnection() 메소드를 사용해서 커넥션을 풀에 복귀시켜야 한다는 점에 주의한다.
만일 close()를 사용하면 사용자 정의 커넥션 풀은 동작하기 않게 된다.

세 번째 JSP 예제

사용자 정의 로그 파일 사용하기

이제까지 우리는 로그 메시지를 출력하기 위해서 System.out.println()를 사용했다.
윈도 시스템에 톰캣을 인스톨러로 설치한 경우 System.out.println()은 CATALINA_HOME/logs 디렉터리의 stdout_로 시작하는 파일에 메시지를 기록한다.

개발할 때나 운영할 때 좀 더 좋은 환경을 제공하기 위해서, 로그 메시지는 프로그래머나 운영자가 원하는 파일에 출력될 필요가 있다.

로깅과 관련해서 오픈 소스가 많이 나와 있다.
하지만 우리는 로깅에 대한 기본 개념을 이해해야 하므로 사용자 정의 로그 파일인 Log.java를 사용하겠다.

getEmp2.jsp 파일을 열고 Save As...메뉴을 사용해서 getEmp3.jsp 파일을 생성한 후 다음과 같이 수정한다.

getEmp3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<%@ page import="net.java_school.db.dbpool.*" %>
<%@ page import="net.java_school.util.*" %>
<jsp:useBean id="dbmgr" class="net.java_school.db.dbpool.OracleConnectionManager" scope="application" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>세 번째 JSP 예제</title>
</head>
<body>
<%
Log log = new Log();
Connection con = null;
Statement stmt = null;
ResultSet rs = null;

String sql = "select * from emp";

try {
	con = dbmgr.getConnection();
	stmt = con.createStatement();
	rs = stmt.executeQuery(sql);

	while (rs.next()) {
		String empno = rs.getString(1);
		String ename = rs.getString(2);
		String job = rs.getString(3);
		String mgr = rs.getString(4);
		String hiredate = rs.getString(5);
		String sal = rs.getString(6);
		String comm = rs.getString(7);
		String depno = rs.getString(8);
		
		out.println( empno + " : " + ename + " : " + job + " : " + mgr + 
		" : " + hiredate + " : " + sal + " : " + comm + " : " + depno + "<br />" );
	}

} catch (SQLException e) {
	log.debug("Error Source:getEmp3.jsp : SQLException");
	log.debug("SQLState : " + e.getSQLState());
	log.debug("Message : " + e.getMessage());
	log.debug("Oracle Error Code : " + e.getErrorCode());
	log.debug("sql : " + sql);
} finally {
	if (rs != null) {
		try {
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	if (stmt != null) {
		try {
			stmt.close();
		} catch (SQLException e) {

			e.printStackTrace();
		}
	}
	if (con != null) {
		dbmgr.freeConnection(con);
	}
	log.close();
}
%>
</body>
</html>

로그 메시지를 확인하기 위해선 catch 블록이 실행되야 한다. getemp3.jsp에서 쿼리문을 String sql = "select * fromemp"로 수정한다. http://localhost:8080/getemp3.jsp를 방문한다. Log.java이 지정한 파일에서 로그 메시지를 확인한다.

Thu Jun 12 14:30:51 KST 2014 : Oracle Error Code : 923
Thu Jun 12 14:30:51 KST 2014 : sql : select * fromemp
Thu Jun 12 14:30:52 KST 2014 : Error Source:getEmp3.jsp : SQLException
Thu Jun 12 14:30:52 KST 2014 : SQLState : 42000
Thu Jun 12 14:30:52 KST 2014 : Message : ORA-00923: FROM keyword not found where expected

네 번째 JSP 예제

GetEmpServlet 서블릿에서는 init() 메소드에서 JDBC 드라이버를 로딩했다. "서블릿" 절에서 봤듯이 init() 메소드는 서블릿이 인스턴스가 된 후 서블릿 컨테이너에 의해 단 한번 자동으로 호출된다. JSP에도 서블릿의 init()에 해당하는 메소드가 있다. 그런 메소드가 JDBC 드라이버를 로딩한다면 JSP 성능은 향상된다. 서블릿의 init()에 해당하는 JSP 메소드는 다음 주소에서 찾을 수 있다. http://docs.oracle.com/javaee/7/api/javax/servlet/jsp/JspPage.html#jspInit()

getEmp3.jsp를 연다. Save As.. 메뉴를 사용하여 getEmp4.jsp를 만든다. 서블릿의 init()에 해당하는 JSP 메소드가 JDBC 드라이버를 로딩하도록 getEmp4.jsp를 수정한다.

getEmp4.jsp는 과제로 남긴다.