4.8 ResultSet에서 값 읽어오기
Statement의 executeQuery() 메서드는 SELECT 퀴리 실행 시 사용되며, SELECT 쿼리의 실행 결과를 java.sql.ResultSet 객체에 담아 리턴.
ResultSet 클래스는 next() 메서드를 제공. 이를 사용하여 SELECT 결과의 존재 여부를 알 수 있다.
ResultSet은 SELECT 쿼리결과를 행으로 저장하며 커서를 통해 각 행의 데이터에 접근. 최초에 커서는 1행 이전에 존재. ResultSet.next() 메서드는 다음 행이 존재할 경우 true를 리턴하고 커서를 그 행으로 이동시킨다. 마지막 행에 커서가 도달하면 next() 메서드는 false를 리턴한다.
ResultSet 클래스의 주요 데이터 읽기 메서드
메서드 | 리턴 타입 | 설명 |
getString(String name) getString(int index) |
String | 지정한 칼럼의 값을 String으로 읽어온다. |
getCharacterStream(String name) getCharacterStream(int index) |
java.io.Reader | 지정한 칼럼의 값을 스트림 형태로 읽어온다. LONG VARCHAR타입을 읽어올 때 사용 |
getInt(String name) getInt(int index) |
int | 지정한 칼럼의 값을 int 타입으로 읽어온다. |
getLong(String name) getLong(int index) |
long | 지정한 칼럼의 값을 long 타입으로 읽어온다. |
getDouble(String name) getDouble(int index) |
double | 지정한 칼럼의 값을 double 타입으로 읽어온다. |
getFloat(String name) getFloat(int index) |
float | 지정한 칼럼의 값을 float 타입으로 읽어온다. |
getTimestamp(String name) getTimestamp(int index) |
java.sql.Timestamp | 지정한 칼럼의 값을 Timestamp 타입으로 읽어온다. SQL TIMESTAMP 타입을 읽어올 때 사용. |
getDate(String name) getDate(int index) |
java.sql.Date | 지정한 칼럼의 값을 Date 타입으로 읽어온다. SQL DATE 타입을 읽어올 때 사용. |
getTime(String name) getTime(int index) |
java.sql.Time | 지정한 칼럼의 값을 Time 타입으로 읽어온다. SQL TIME 타입을 읽어올 때 사용. |
ResultSet의 get 계열의 메서드는 현재 커서에서 데이터를 읽어오기 때문에 처음에 next() 메서드를 사용해서 커서를 이동시켜야 한다.
rs = stmt.executeQuery("select * from member"); if (rs.next()) { //rs.next()에 의해 다음 행(첫번째 행)으로 이동 String name = rs.getSting("NAME"); ... } else { //첫 번째 행이 존재하지 않는다. 즉, 결과가 없다 }
1개 이상의 행을 처리할 때에는 while이나 do-while 구문을 사용.
rs = stmt.executeQuery(...); while(rs.next()) { //한 행씩 반복 처리 String name = rs.getSting(1); ... }
rs = stmt.executeQuery(...); if(rs.next) { do { String name = rs.getSting("NAME"); ... } while (rs.next()); }
간단하게 파라미터로 아이디를 전달받으면 MEMBER테이블로부터 해당 회원 정보를 읽어와 출력해주는 JSP페이지
[리스트 12.4 chap12\viewMember.jsp]
<%@ page contentType = "text/html; charset=euc-kr" %> <%@ page import = "java.sql.DriverManager" %> <%@ page import = "java.sql.Connection" %> <%@ page import = "java.sql.Statement" %> <%@ page import = "java.sql.ResultSet" %> <%@ page import = "java.sql.SQLException" %> <% String memberID = request.getParameter("memberID"); %> <html> <head><title>회원 정보</title></head> <body> <% Class.forName("com.mysql.jdbc.Driver"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try { String jdbcDriver = "jdbc:mysql://localhost:3306/chap12?" + "useUnicode=true&characterEncoding=euckr"; String dbUser = "jspexam"; String dbPass = "jspex"; String query = "select * from MEMBER where MEMBERID = '"+memberID+"'"; conn = DriverManager.getConnection(jdbcDriver, dbUser, dbPass); stmt = conn.createStatement(); rs = stmt.executeQuery(query); if( rs.next() ) { %> <table border="1"> <tr> <td>아이디</td><td><%= memberID %></td> </tr> <tr> <td>암호</td><td><%= rs.getString("PASSWORD") %></td> </tr> <tr> <td>이름</td><td><%= rs.getString("NAME") %></td> </tr> <tr> <td>이메일</td><td><%= rs.getString("EMAIL") %></td> </tr> </table> <% } else { %> <%= memberID %>에 해당하는 정보가 존재하지 않습니다. <% } } catch(SQLException ex) { %> 에러 발생: <%= ex.getMessage() %> <% } finally { if (rs != null) try { rs.close(); } catch(SQLException ex) {} if (stmt != null) try { stmt.close(); } catch(SQLException ex) {} if (conn != null) try { conn.close(); } catch(SQLException ex) {} } %> </body> </html>
- 라인 35 : 데이터가 존재하면 라인 36~51의 블록 실행
- 라인 52~56 : 라인 35의 rs.next()가 false를 리턴하는 경우 실행
- 라인 62 : ResultSet도 Close() 해서 자원을 반환해야 함
웹 브라우저에 다음 URL을 입력해서 실행해봐.
http://localhost:8080/chap12/viewMember.jsp?memberID=kidhuk
4.9 ResultSet에서 LONG VARCHAR 타입 값 읽어오기
SQL의 LONG VARCHAR 타입은 대량의 텍스트 데이터를 저장할 때 사용. 이를 읽어오기 위해서는 getCharacterStream() 메서드 사용
LONG VARCHAR 타입의 값을 읽어오는 코드
String data = null; //스트림으로 읽어온 데이터를 저장 java.io.reader reader = null; //LONG VARCHAR 데이터를 읽어올 스트림 try { reader = rs.getCharacterStream("FIELD"); //스트림을 읽어옴 if (reader != null) { //스트림에서 읽어온 데이터를 저장할 버퍼 StringBuffer buff = new StringBuffer(); char[] ch = new char[512]; int len = -1; //스트림(reader)에서 데이터를 읽어와 버퍼(StringBuffer)에 저장한다. while ( (len = reader.read(ch)) != -1) { buffer.append(ch, 0, len); } //버퍼에 저장된 내용을 String으로 변환 data = buffer.toString(); } } catch(IOException ex) { //예외발생 (reader.read() 메서드는 IOException을 발생시킬 수 있다. } finally { if (reader != null ) try { reader.close(); } catch(IOException ex) {} } // ... data를 사용
LONG VARCHAR 타입의 칼럼을 포함하고 있는 간단한 테이블 생성
create table MEMBER_HISTORY (
MEMBERID VARCHAR(10) NOT NULL PRIMARY KEY,
HISTORY LONG VARCHAR
)
memberID 파라미터로 입력 받은 값을 사용하여 정보를 출력해주는 JSP페이지
[리스트 12.5 chap12\viewMemberHistory.jsp]
<%@ page contentType = "text/html; charset=euc-kr" %> <%@ page import = "java.sql.DriverManager" %> <%@ page import = "java.sql.Connection" %> <%@ page import = "java.sql.Statement" %> <%@ page import = "java.sql.ResultSet" %> <%@ page import = "java.sql.SQLException" %> <%@ page import = "java.io.Reader" %> <%@ page import = "java.io.IOException" %> <% String memberID = request.getParameter("memberID"); %> <html> <head><title>회원 정보</title></head> <body> <% Class.forName("com.mysql.jdbc.Driver"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try { String jdbcDriver = "jdbc:mysql://localhost:3306/chap12?" + "useUnicode=true&characterEncoding=euckr"; String dbUser = "jspexam"; String dbPass = "jspex"; String query = "select * from MEMBER_HISTORY "+ "where MEMBERID = '"+memberID+"'"; conn = DriverManager.getConnection(jdbcDriver, dbUser, dbPass); stmt = conn.createStatement(); rs = stmt.executeQuery(query); if(rs.next()) { %> <table border="1"> <tr> <td>아이디</td><td><%= memberID %></td> </tr> <tr> <td>히스토리</td> <td> <% String history = null; Reader reader = null; try { reader = rs.getCharacterStream("HISTORY"); if (reader != null) { StringBuffer buff = new StringBuffer(); char[] ch = new char[512]; int len = -1; while( (len = reader.read(ch)) != -1) { buff.append(ch, 0, len); } history = buff.toString(); } } catch(IOException ex) { out.println("예외 발생:"+ex.getMessage()); } finally { if (reader != null) try { reader.close(); } catch(IOException ex) {} } %> <%= history %> </td> </tr> </table> <% } else { %> <%= memberID %> 회원의 히스토리가 없습니다. <% } } catch(SQLException ex) { %> 에러 발생: <%= ex.getMessage() %> <% } finally { if (rs != null) try { rs.close(); } catch(SQLException ex) {} if (stmt != null) try { stmt.close(); } catch(SQLException ex) {} if (conn != null) try { conn.close(); } catch(SQLException ex) {} } %> </body> </html>
http://localhost:8080/chap12/viewMemberHistory.jsp?memberID=madmax
[note.LONG VARCHAR 타입과 DBMS]
오라클에서는 LONG VARCHAR를 LONG으로 표시하고 있기에 ResultSet의 getCharacterStream() 메서드 대신 getLong() 메서드를 사용하지 않게 주의.
MySQL에서는 MEDIUMTEXT, TINYTEXT, TEXT, LONGTEXT의 네 가지 TEXT타입을 지원, 읽어올 때는 getCharacterStream() 메서드 사용
다수의 JDBC 드라이버는 getString()메서드를 사용해서 읽어올 수 있다. 그런 경우 복잡한 코드를 사용할 필요없이 getString() 메서드를 사용하는게 좋다.
4.10 Statement를 이용한 쿼리 실행 시 작은따옴표 처리
“king’s choice”
update TABLENAME set SOMEFIELD = ‘king’’s choice’ where …
이처럼 작은따옴표 두 개로 처리
replaceAll() 메서드 사용
String value = "king's choice"; String replaced = value.replaceAll("'", "''");
Statement를 이용해서 SQL쿼리를 생성할 경우
String name = request.getParameter("name"); String query = "select * from member where name = '" + name.replace("'", "''") + "'";
4.11 PreparedStatement를 사용한 쿼리 실행
java.sql.PreparedStatement는 java.sql.Statement와 동일한 기능을 제공. 차이점은 PreparedStatment는 SQL 쿼리의 틀을 미리 생성해 놓고 값을 나중에 지정한다는 것.
PreparedStatement의 사용순서
- Connection.prepareStatement() 메서드를 사용하여 PreparedStatement 생성
- PreparedStatement의 set메서드를 사용하여 필요한 값 지정
- PreparedStatement의 executeQuery() 또는 executeUpdate() 메서드를 사용하여 쿼리를 실행
- finally 블록에서 사용한 PreparedStatement를 닫는다(close() 메서드 실행)
PreparedStatement를 생성할 때엔 다음과 같이 값부분을 물음표(?)로 대치한 쿼리를 미리 입력한다.
PreparedStatement pstmt = null; ... pstmt = conn.prepareStatement( "insert into MEMBER (MEMBERID, NAME,EMAIL ) values (?, ?, ?)");
그 다음 PreparedStatement가 제공하는 set계열의 메서드를 사용하여 물음표를 대체할 값을 지정한다.
pstmt.setString(1, "madmax"); //첫 번째 물음표의 값 지정 pstmt.setString(2, "매드맥스"); //두 번째 물음표의 값 지정 ...
PreparedStatement 클래스가 제공하는 set메서드
메서드 | 설명 |
setString(int index, String x) | 지정한 인덱스의 파라미터 값을 x로 지정 |
setCharacterStream(int index, Reader reader, int length) | 지정한 인덱스의 파라미터 값을 LONG VARCHAR타입으로 지정할 때 사용. Reader는 값을 읽어올 스트림이며, length는 지정한 문자열의 길이를 나타냄 |
setInt(int index, int x) | 지정한 인덱스의 파라미터 값을 int 값 x로 지정 |
setLong(int index, long x) | 지정한 인덱스의 파라미터 값을 long 값 x로 지정 |
setDouble(int index, double x) | 지정한 인덱스의 파라미터 값을 double 값 x로 지정 |
setFloat(int index, float x) | 지정한 인덱스의 파라미터 값을 float 값 x로 지정 |
setTimestamp(int index, Timestamp x) | 지정한 인덱스의 파라미터 값을 SQL TIMESTAMP 타입을 나타내는 java.sql.Timestamp 타입으로 지정 |
setDate(int index, Date x) | 지정한 인덱스의 파라미터 값을 SQL DATE 타입을 나타내는 java.sql.Date 타입으로 지정 |
setTime(int index, Time x) | 지정한 인덱스의 파라미터 값을 SQL TIME 타입을 나타내는 java.sql.Time 타입으로 지정 |
set계열의 메서드를 사용하여 물음표에 해당하는 값을 모두 지정하면 다음 두 메서드를 사용하여 쿼리를 실행. PreparedStatement를 생성할 때에 실행할 쿼리를 지정하기 때문에 이 두 메서드는 쿼리를 인자로 입력받지 않는다.
- ResultSet executeQuery() : SELECT 쿼리 실행. ResultSet을 결과값으로 리턴
- int executeUpdate() : INSERT, UPDATE, DELETE 실행. 실행결과 변경된 레코드의 개수 리턴
PreparedStatement 클래스를 사용하여 MEMBER테이블에 값을 삽입하는 예제
[리스트 12.6 chap12\insert\insertFrom.jsp]
<%@ page contentType = "text/html; charset=euc-kr" %> <html> <head><title>MEMBER 테이블 레코드 삽입</title></head> <body> <form action="/chap12/insert/insert.jsp" method="post"> <table border="1"> <tr> <td>아이디</td> <td><input type="text" name="memberID" size="10"></td> <td>암호</td> <td><input type="text" name="password" size="10"></td> </tr> <tr> <td>이름</td> <td><input type="text" name="name" size="10"></td> <td>이메일</td> <td><input type="text" name="email" size="10"></td> </tr> <tr> <td colspan="4"><input type="submit" value="삽입"></td> </tr> </table> </form> </body> </html>
http://localhost:8080/chap12/insert/insertForm.jsp
[리스트 12.7 chap12\insert\insert.jsp]
<%@ page contentType = "text/html; charset=euc-kr" %> <%@ page import = "java.sql.DriverManager" %> <%@ page import = "java.sql.Connection" %> <%@ page import = "java.sql.PreparedStatement" %> <%@ page import = "java.sql.SQLException" %> <% request.setCharacterEncoding("euc-kr"); String memberID = request.getParameter("memberID"); String password= request.getParameter("password"); String name = request.getParameter("name"); String email = request.getParameter("email"); Class.forName("com.mysql.jdbc.Driver"); Connection conn = null; PreparedStatement pstmt = null; try { String jdbcDriver = "jdbc:mysql://localhost:3306/chap12?" + "useUnicode=true&characterEncoding=euckr"; String dbUser = "jspexam"; String dbPass = "jspex"; conn = DriverManager.getConnection(jdbcDriver, dbUser, dbPass); pstmt = conn.prepareStatement( "insert into MEMBER values (?, ?, ?, ?)"); pstmt.setString(1, memberID); pstmt.setString(2, password); pstmt.setString(3, name); pstmt.setString(4, email); pstmt.executeUpdate(); } finally { if (pstmt != null) try { pstmt.close(); } catch(SQLException ex) {} if (conn != null) try { conn.close(); } catch(SQLException ex) {} } %> <html> <head><title>삽입</title></head> <body> MEMBER 테이블에 새로운 레코드를 삽입했습니다 </body> </html>
- 라인 28~29 : 실행할 쿼리를 생성
- 라인 30~33 : 쿼리의 각 물음표에 알맞은 값을 지정
- 라인 35 : 쿼리를 실행
4.12 PreparedStatement에서 LONG VARCHAR 타입 값 지정하기
setCharaterStream(int index, Reader reader, int length)
setCharaterStream()메서드는 Reader로 부터 length 글자 수 만큼 데이터를 읽어와 저장.
setCharaterStream()메서드를 이용해서 String타입의 값을 저장하고 싶은 경우 StringReader를 이용
PreparedStatement pstmt = null; try { String value = "......"; //LONG VARCHAR에 넣을 값 pstmt = conn.prepareStatement(...); java.io.StringReader reader = new java.io.StringReader(value); pstmt.setCharacterStream(1, reader, value.length()); ... } catch(SQLException ex) { ... } finally { ... if (pstmt != null) try {pstmt.close(); } catch(SQLException ex) {} }
텍스트파일로부터 데이터를 읽어와 저장하고 싶다면 다음과 같이 FileReader를 이용
PreparedStatement pstmt = null; FileReader reader = null; try { pstmt = conn.prepareStatement(...); reader = new java.io.FileReader(파일경로); pstmt.setCharacterStream(1, reader); ... } catch(SQLException ex) { ... } catch(IOException ex) { ... } finally { ... if (pstmt != null) try {pstmt.close(); } catch(SQLException ex) {} if (reader != null) try {reader.close(); } catch(IOException ex) {} }
4.13 PreparedStatment 쿼리를 사용하는 이유
- 반복해서 실행되는 동일 쿼리의 속도를 증가시키기 위해
- 값 변환을 자동으로 하기 위해
- 간결한 코드를 위해
먼저 속도
Statement를 사용할 경우 다음의 경우 3번의 쿼리분석 및 실행이 발생
stmt = conn.createStatement(); stmt.executeQuery("select * from MEMBER where MEMBERID = '"+abc+"'"); stmt.executeQuery("select * from MEMBER where MEMBERID = '"+def+"'"); stmt.executeQuery("select * from MEMBER where MEMBERID = '"+ghi+"'");
PreparedStatement를 사용할 경우 한번의 쿼리분석 및 3번의 실행
pstmt = conn.prepareStatement("select * from MEMBER where MEMBERID = ?"); pstmt.setString(1, "abc"); pstmt.executeQuery(); pstmt.setString(1, "def"); pstmt.executeQuery(); pstmt.setString(1, "ghi"); pstmt.executeQuery();
두번째 값 자동변환
Statement를 사용할 경우 작은따옴표가 포함될 때 작은따옴표 두번으로 변경해줘야함
stmt.executeQuery("select * from MEMBER where name = '"+ "mad'max".replace("'","''") + "'";
PreparedStatement의 경우 setString()메서드가 알아서 작은따옴표를 처리해줌
pstmt.setString(1,"mad'max");
TIMESTAMP,DATE,TIME의 경우 Statement의 경우 DBMS마다 다르게 코딩해줘야 하나 PreparedStatement를 사용하면 다음과 같은 동일한 코드를 사용하게 된다.
Timestamp time = new Timestamp(System.currentTimeMillis()); pstmt.setTimestamp(3, time);
세번째 코드가 깔끔해진다. Update 코드의 예
stmt.executeQuery("update member set NAME = '"+name+"' where "+ "MEMBERID = '"+ id + "'"); PreparedStatemet pstmt = conn.prepareStatemet( "update member set NAME = ? where MEMBERID = ?"); pstmt.setString(1, name); pstmt.setString(2, id);
4.14 오라클 CLOB 타입 사용하기
오라클의 경우 대량의 텍스트 데이터를 저장할 때 LONG VARCHAR에 해당하는 LONG타입을 지원해 왔지만, LONG타입보다 CLOB타입을 이용할 것을 권하고 있다.
(1) 오라클 10g 이전 버전에서의 CLOB 타입 처리
CLOB타입의 칼럼에 데이터를 삽입하려면 다음의 순서에 따라 데이터를 입력해주어야 함.
- INSERT쿼리를 실행할 때 CLOB 칼럼에 삽입될 값으로 EMPTY_CLOB()함수를 지정한다.
- INSERT쿼리 실행 후 수정 모드 SELECT 쿼리를 이용해서 CLOB칼럼 타입의 값을 읽어온다.
- ResultSet.getClob()을 이용해서 CLOB 칼럼 타입에 데이터를 쓰기 위한 캐릭터 스트림(Writer)을 구한다.
- 캐릭터 스트림을 이용해서 CLOB칼럼에 삽입할 데이터를 삽입한다.
code1
code2
code3
(2) 오라클 10g에서의 CLOB 타입 처리
code4
code5
4.15 웹 어플리케이션 구동 시 JDBC 드라이버 로딩하기
지금까지 다음과 같이 JDBC 드라이버을 로딩했었다.
Class.forName(jdbcDriverClass);
하지만, JDBC 드라이버는 한 번만 로딩하면 이후로 계속해서 사용할 수 있기 때문에 모든 JSP페이지에서 매번 JDBC 드라이버를 로딩할 필요가 없다.
JDBC 드라이버를 로딩하기에 가장 좋은 시점은 웹 어플리케이션이 시작할 때이다. 즉, 톰캣이나 제티 같은 웹 컨테이너가 시작될 때 자동으로 JDBC 드라이버를 로딩하도록 지정하면 JSP 페이지에서 매번 JDBC 드라이버를 로딩할 필요가 없어진다.
웹 어플리케이션이 시작될 때 자동으로 JDBC 드라이버를 로딩하도록 만들려면 다음과 같은 서블릿 클래스를 사용하면 된다.
[리스트 12.8 chap12\WEB-INF\src\kame\jdbc\loader\Loader.java
package kame.jdbc.loader; import javax.servlet.http.HttpServlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import java.util.StringTokenizer; public class Loader extends HttpServlet { public void init(ServletConfig config) throws ServletException { try { String drivers = config.getInitParameter("jdbcdriver"); StringTokenizer st = new StringTokenizer(drivers, ","); while (st.hasMoreTokens()) { String jdbcDriver = st.nextToken(); Class.forName(jdbcDriver); } } catch(Exception ex) { throw new ServletException(ex); } } }
- 라인 10 : 서블릿이 초기화될 때 호출되는 init() 메서드 구현
- 라인 12 : jdbcdriver 초기화 파라미터 값 읽어 옴
- 라인 13 : 초기화 파라미터의 값을 콤마로 분리
- 라인 14~17 : 콤마로 분리된 각 문자열을 JDBC 드라이버 클래스의 이름으로 사용해서 JDBC 드라이버를 로딩
- 라인 18~20 : JDBC 드라이버 로딩 과정에서 문제가 있을 경우 예외를 발생시킨다.
소스 코드 작성후 다음과 같은 명령어를 이요해 Loader.java를 컴파일하자. 제티를 사용하고 있다면 [톰캣]\lib\servlet-api.jar 파일 대신 [제티]\lib\servlet-api-2.5-6.1.xx.jar파일을 클래스 패스에 추가해주면 된다.
C:\>set CLASSPATH=C:\Program Files\apache-tomcat-7.0.23\lib\servlet-api.jar C:\>cd C:\Program Files\apache-tomcat-7.0.23\webapps\chap12\WEB-INF C:\...\chap12\WEB-INF>javac -d classes src\kame\jdbc\loader\Loader.java
javac를 실행하면 chap12\WEB-INF\src\kame\jdbc\loader 디렉터리에 Loader.class파일이 생성될 것이다.
Loader.java를 컴파일 한 다음에는 웹 어플리케이션이 시작될 때 자동으로 Loader 서블릿클래스가 실행되도록 설정해 주어야 한다. 이를 위해 다음과 같이 web.xml 파일에 <servlet> 태그를 추가해 주면 된다.
[리스트 12.9 chap12\WEB-INF\web.xml
<?xml version="1.0" encoding="euc-kr"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>JDBCDriverLoader</servlet-name> <servlet-class>kame.jdbc.loader.Loader</servlet-class> <init-param> <param-name>jdbcdriver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> </web-app>
- 라인 10~15 : Loader 서블릿 클래스에 대한 추가설정
- 라인 11~14 : 초기화 파라미터 jdbcdriver의 값을 com.mysql.jdbc.Driver로 지정한다.(<init-param> 태그를 사용하여 지정한 초기화 파라미터는 ServletConfig.getInitParameter()메서드를 통해서 읽어올 수 있다. [리스트 12.8] Loader.java의 라인 12를 참고)
- 라인 15 : 웹 어플리케이션이 시작될 때, Loader 서블릿 클래스를 자동으로 로딩한다.
code08
'JSP' 카테고리의 다른 글
03. 웹 어플리케이션 개발 시작하기 (0) | 2012.02.23 |
---|---|
04-1. JSP에서 JDBC프로그래밍하기 1 (0) | 2012.02.16 |
02. 예제 실행을 위한 데이터베이스 생성 (0) | 2012.02.16 |
chap13. 웹 어플리케이션의 일반적인 구성 및 방명록 구현 (0) | 2012.02.16 |
03. SQL 기초 (0) | 2012.02.16 |