본문 바로가기
Web

데이터베이스 프로그래밍 기초 (2)

by jaee_ 2021. 9. 28.
본 글을 📚 최범균의 JSP2.3 웹 프로그래밍 을 읽고 정리한 글입니다. 

 

JSP 에서 JDBC 프로그래밍 하기

자바에서 데이터베이스 프로그래밍을 할 때에는 JDBC API를 사용한다. JDBC는 Java DataBase Connectivity의 약자로서 자바에서 데이터베이스와 관련된 작업을 처리할 때 사용하는 API이다. 자바는 DBMS의 종류에 상관없이 하나의 JDBC API를 사용해서 데이터베이스 작업을 처리할 수 있기 때문에, 일단 익혀두면 모든 DBMS에 대해서 동일한 방식으로 데이터베이스 작업을 처리할 수 있게 된다. 

 

1.1 JDBC의 구조

JDBC 프로그래밍의 구조

JSP를 포함한 자바 어플리케이션에서 데이터베이스를 사용할 떄에는 데이터베이스 종류에 상관없이 JDBC API를 이용해서 데이터베이스에 접근한다. 각각의 DBMS는 자신에게 알맞는 JDBC 드라이버를 제공하고 있다. JDBC API는 JDBC 드라이버를 거려 데이터베이스와 통신을 한다. 

 

JDBC API를 사용하면 DBMS에 알맞는 드라이버만 있으면 어떤 데이터베이스라도 사용할 수 있다. 

 

1.2 JDBC 프로그래밍의 코딩 스타일

JDBC 프로그램의 전형적인 실행 순서는 다음과 같다.

  1. JDBC 드라이버 로딩
  2. 데이터베이스 커넥션 구함
  3. 쿼리 실행을 위한 Statement 객체 생성
  4. 쿼리 실행
  5. 쿼리 실행 결과 사용
  6. Statement 종료
  7. 데이터베이스 커넥션 종료

아래의 코드를 보고 확인해보자 (MEMBER 테이블이 있다는 가정하에 실행한다)

<%@ page import="java.sql.Connection" %>
<%@ page import="java.sql.Statement" %>
<%@ page import="java.sql.ResultSet" %>
<%@ page import="java.sql.Driver" %>
<%@ page import="java.sql.DriverManager" %>
<%@ page import="java.sql.SQLException" %>
<%--
  Created by IntelliJ IDEA.
  User: yeonjae
  Date: 2021-09-27
  Time: 오후 9:42
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>회원 목록</title>
</head>
<body>
MemberList (회원 목록)
<table>
    <tr>
        <td>이름</td>
        <td>아이디</td>
        <td>이메일</td>
    </tr>

    <%
        // 1. JDBC 드라이버 로딩
        Class.forName("com.mysql.jdbc.Driver");

        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        String jdbcDriver = "jdbc:mysql://localhost:8080/JSP?useUnicode=true&characterEncoding=utf8";
        String dbUser = "username";
        String dbPw = "pw";
        String query = "SELECT * FROM MEMBER ORDER BY MEMBERID";

        try {
            // 2. 데이터베이스 커넥션 생성
            conn = DriverManager.getConnection(jdbcDriver, dbUser, dbPw);

            // 3. Statement 생성
            stmt = conn.createStatement();

            // 4. 쿼리 실행
            rs = stmt.executeQuery(query);

            // 5. 쿼리 실행 결과 출력
            while (rs.next()) {
    %>
    <tr>
        <td><%= rs.getString("NAME") %>
        </td>
        <td><%= rs.getString("MEMBERID") %>
        </td>
        <td><%= rs.getString("EMAIL") %>
        </td>
    </tr>
    <%
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6. 사용한 Statement 종료
            if (rs != null) { try {rs.close();} catch (SQLException e) {  e.printStackTrace();}}
            if (stmt != null) { try {stmt.close();} catch (SQLException e) {  e.printStackTrace();}}

            //7. 커넥션 종료
            if (conn != null) { try {conn.close();} catch (SQLException e) {  e.printStackTrace();}}
            }
    %>
</table>
</body>
</html>

실행 후 에러가 'Access denied for'로 시작한다면 다음 중 하나를 확인하길 바란다.

  • 데이터베이스를 올바르게 생성했는지
  • 데이터베이스에 접근할 때 사용한 DB계정과 암호가 맞는지
  • 해당 계정이 데이터베이스에 접근할 권한이 있는지 

 

1.3 DBMS와의 통신을 위한 JDBC드라이버

JDBC는 DBMS와의 통신을 담당하는 자바 클래스로서 DBMS마다 별도의 JDBC 드라이버가 필요하다. 각 DBMS는 JDBC 드라이버를 jar 파일 형태로 제공한다. 

 

JDBC 드라이버를 로딩하는 방법은 다음과 같다.

try{
	Class.forName("JDBC 드라이버 클래스의 완전한 이름");
}catch(ClassNotFoundException e){
	// 지정한 클래스가 존재하지 않는 경우 에러가 발생한다. 
}

다음은 주요 데이터베이스의 JDBC 드라이버에 해당하는 클래스이다. 

MySQL : com.mysql.jdbc.Driver
Oracle : oracle.jdbc.driver.OracleDriver
Ms SQL 서버 : com.microsoft.sqlserver.jdbc.SQLServerDriver

Class.forName() 메서드는 지정한 클래스 정보를 담고 있는 Class인스턴스를 구해지는 기능만을 제공한다. JDBC 드라이버에 해당하는 클래스들을 Class.forName() 메서드를 통해 로딩될 때 자동으로 JDBC 드라이버로 등록한다. 

 

 

1.4 데이터베이스 식별을 위한 JDBC URL

데이터베이스를 구분할 때 URL 과 비슷한 형식을 갖는 JDBC URL을 사용한다. 사용하는 JDBC 드라이버에 따라서 JDBC URL 표현 방식에 차이가 나는데 일반적인 형식은 다음과 같다.

jdbc:[DBMS]:[데이터베이스식별자]

oracle의 경우 : jdbc:oracle:thin:@HOST:PORT:SID
MySQL의 경우 : jdbc:mmysql://HOST[:PORT]/DBNAME[?param=value&param=value...]

HOST와 PORT는 각각 호스트의 주소와 포트번호를 나타낸다. 

더보기

오라클 드라이버에는 Thin 드라이버와 OCI드라이버가 있다. Thin 드라이버는 자바 언어로만 구현된 JDBC드라이버로서 JDK만 설치되어 있으면 어디서든 사용할 수 있다. 반면에 OCI 드라이버는 네이티브 모듈을 사용하는 드라이버로서 해당 모듈을 설치해주어야만 사용할 수 있다. 위의 예제에서 jdbc:oracle:thin과 같이 thin이 포함된 것은 Thin드라이버를 사용해서 연결함을 뜻한다. 

 

1.5 데이터베이스 커넥션

 

JDBC를 이용해서 데이터베이스를 사용하려면 데이터베이스와 연결된 커넥션을 구해야한다. java.sql.Connection 타입이 데이터베이스 커넥션을 나타내며, java.sql.DriverManager 클래스가 제공하는 getConnection() 메소드를 사용해서 커넥션을 구할 수 있다. DriverManager 클래스는 다음의 두 getConnection() 메소드를 제공하고 있다.

  • DriverManager.getConnection(String jdbcURL)
  • DriverManager.getConnection(String jdbcURL, String user, String password)

getConnection() 메서드의 jdbcURL은 데이터베이스에 연결할 때 사용할 JDBC URL을 나타내며, user와 password는 데이터베이스의 계정과 암호를 뜻한다. 

 

위의 getConnection()이 제대로 연결이 되고나면 Connection 객체를 리턴한다. 하지만 정상적으로 연결이 되지 않았을 경우 Connection 객체를 리턴하지 않고 SQLException 에러를 발생시킨다. 때문에 반드시 try-catch로 SQLException 처리를 해주어야 한다. 

 

또한, Connection 객체를 모두 사용한 뒤에는 close() 메서드를 반드시 호출해서 Connection 객체가 사용한 자원을 반환해야 한다. 그렇지 않으면 시스템 자원이 불필요하게 소모된다. 메모리 누락이 발생한다는 이야기다. 그러니 전부 사용한 후에는 객체가 null인지 확인 후에 null이 아니라면 닫아주는 코드를 작성하자. 

 

1.6 Statement를 사용한 쿼리 실행

Connection 객체를 생성한 후에는 Connection 객체로부터 Statement를 생성하고 쿼리를 실행할 수 있다. Statement는 다음과 같이 Connection의 createStatement() 메서드를 사용하여 생성한다. 

Statement stmt = conn.createStatement();

Statement 객체를 사용하면 쿼리를 실행할 수 있다. 쿼리를 실행할 때 사용하는 메소드는 다음과 같다. 

  • ResultSet executeQuery(String query) : SELECT 쿼리를 실행한다.
  • int executeUpdate(String query) : INSERT, DELETE, UPDATE 쿼리를 실행한다. 

executeQuery는 결과값을 java.sql.ResultSet 객체에 저장해서 리턴한다. 

 

1.7 ResultSet에서 값 읽어오기

Statement의 executeQuery() 메서드는 SELECT 쿼리를 실행할 때 사용되며, SELECT 쿼리의 실행 결과를 java.sql.ResultSet 객체에 담아서 리턴한다. 따라서, ResultSet이 제공하는 메서드를 사용해서 결과값을 읽어올 수 있다.

 

ResultSet 클래스는 next() 메서드를 제공하는데, next() 메서드를 사용해서 SELECT 결과의 존재 여부를 확인할 수 있다. 

사진 출처 : https://velog.io/@jsj3282/

위의 사진에 대해서 설명하겠다. ResultSet은 SELECT 의 쿼리 결과를 위 사진처럼 저장하며 커서를 통해서 각 행의 데이터에 접근한다. 최초의 커서는 1행 이전에 존재한다. ResultSet.next() 메서드는 커서의 다음 행이 존재하는 경우 true를 리턴하고 커서를 그 행으로 이동시킨다. 마지막행에 커서가 도달하면 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 타입을 읽어올 때 사용한다.

 

댓글