본문 바로가기
  • hello world
Language/Java

[Java] JDBC

by JJoajjoa 2023. 10. 6.

 

Java DataBase Connectivity

자바 프로그래밍 언어에서 데이터베이스 접근 할 수 있게 해주는 API

Java SE 기술 중 일부 

→ 자바 응용 프로그램이 다양한 데이터베이스 접근 할 수 있게 해주는 Java Standard API

SQL 기반의 데이터베이스 유니버셜 접근 방식을 제공

개발자들은 RDBMS에 연결하고 SQL을 실행 가능함

 

 

 

주요 목적

1. 데이터 베이스 독립성

개발자가 특정 데이터베이스에 종속되지 않고, 여러가지 데이터베이스 시스템과 상호작용 할 수 있도록 도와줌

>> Oracle, MySql, MariaDB, PostgreSQL 등 다양한 RDBMS와 연결할 수 있도록 도와줌

 

2. SQL 지원

Structured Query Language를 사용하여 데이터 조작 및 검색을 도와줌

 

3. 데이터 엑세스 간소화

JDBC API를 사용하면 복잡한 네트워크 프로토콜이나 DBMS 인터페이스를 직접 처리하지 않아도 됨

 

 

 

작동 프로세스

1. 드라이버 로드

RBDMS 에 맞는 JDBC 드라이버를 로드함

이 드라이버는 해당 DBMS와 어플리케이션 사이 통신역할을 함

>> 연결 통신을 하기위한 첫단계

 

2. 연결생성

드라이버가 로드되면 DriverManager.getConnection() 메서드를 호출하여 DBMS와의 연결을 생성함

 

3. SQL 문장 실행

Connection 객체로부터 Statement 또는 PreparedStatement 객체를 생성하고

executeQuery() 또는 executeUpdate() 메서드로 SQL 문장을 실행

 

4. 결과처리

SELECT 문의 결과인 ResultSet 객체에서 데이터 읽기 및 처리,

INSERT/UPDATE/DELETE문을 처리할 수있음

 

5. 연결 종료 

Connection 객체 닫기

 

 

 

JDBC 드라이버 로드

JDBC 드라이버는 각 데이터베이스 벤더에 따르며

해당 벤더에서 제공하는 라이브러리 형태로 제공됨

 

Oracle 데이터베이스에 접속 하려면 Oracle JDBC 드라이버 필요

MySQL에 접속하려면 MySQL JDBC 드라이버 필요

 

1. Class.forName()

가장 일반적인 방식

특정 클래스 이름을 문자열로 전달하고 클래스를 메모리에 로드함

해당 클래스의 정적 초기화 블록(static initializer)이 실행되며

대부분 JDBC 드라이버는 이 초기화 블록에서 자신을 DriverManager에 등록함

Class.forName("com.mysql.jdbc.Driver");

△ mysql 부분은 다른걸로 바꿔도 됨

 

2. DriverManager.reqisterDriver()

직접 Driver 객체를 생성하여 DriverManager에 등록하는 방식

DriverManager.registerDriver(new com.mysql.jdbc.Driver());
public class NewServletContextListener implements ServletContextListener {
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        Enumeration<Drive> drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
            } catch (SQLException e) {
                sysout(e);
            }
        }
    }
}

 

목표 : 

두가지 모두

JVM 내부에 사용 가능한 JDBC 드라이버 리스트에

특정 데이터베이스 연결을 위한 JDBC 드라이버를 등록하는 것!

 

 

 

 

데이터베이스 연결 

DriverManager.getConnection() 메서드를 사용하여 데이터베이스에 연결

>> 데이터베이스 URL, 사용자 이름, 비밀번호

Connection conn = DriverManager.getConnection(url, username, password);

△ username과 password는 데이터베이스의 계정과 비밀번호

 

url은 특정 데이터베이스 시스템에 접근하기 위한 문자열 jdbc:subprotocol:subname 형식

▷ MySQL   jdbc:mysql://localhost:3306/mydb
Oracle   jdbc:oracle:thin:@localhost:1521:mydb

 

데이터베이스 연결 과정에서 여러가지 오류(네트워크 문제, 잘못된 인증정보 등)이 발생할 수 있어서

예외처리도 함께 써주는 것이 좋음

try {
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "MySQL", "0000");
} catch (SQLException e) {
    sysout(e);
}

 

 

 

결과 처리

▶ SELECT 문

>> ResultSet 객체

데이터베이스에서 정보를 조회하는데 사용됨

이 결과가 ResultSet 객체에 반환됨

ResultSet 은 테이블 형태의 구조로 각 열은 테이블 칼럼, 각 행은 하나의 레코드를 나타냄

ResultSet.next() 메서드를 사용하여 각 행을 순회하고, get 메서드를 사용하여 해당 행 컬럼 값을 추출 할 수 있음

State stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM employees")

while (rs.next()) {
    String name = rs.getString("name");
    int age = rs.getInt("age");
}

 

DML문(INSERT, UPDATE, DELETE 등)

>> 영향받은 행의 갯수

데이터베이스 내부를 변경하는데 사용되며 이러한 쿼디들이 영향받은 행의 수

이 값은 executeUpdate() 메서드 호출 시 정수형으로 반환됨

Statement stmt = conn.createStatement();
int affectedRows = stmt.executeUpdate("UPDATE employees SET age = 30 WHERE name = 'Adam'");

System.out.println(affectedRows + " rows updated.");

 

 

 

 

자원 해제

JDBC를 사용하여 데이터베이스 작업을 하면 여러가지 리소스가 발생함

Connection, Statement, PreparedStatement, ResultSet

>> 이러한 객체들이 모두 시스템 리소스를 차지함

>> 사용 후 해제하지 않으면 메모리 누수와 같은 문제가 발생할 수 있음

>> 각 객체별로 close() 메서드가 있음

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

try {
    //데이터베이스 작업
} catch (SQLExceptiion e) {
    //예외처리
} finally {
    if(rs != null) { # 순서 중요!!!!!
        try {
            rs.close();
        } catch (SQLException e) {}
    }
    if(stmt != null) {
        try {
            rs.close();
        } catch (SQLException e) {}
    }
    if(conn != null) {
        try {
            rs.close();
        } catch (SQLException e) {}
    }
}

 

▼ 자바 7 부터는 try-with-resource 라는 구문을 사용할 수 있음

try (
    Connect conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "username", "password");
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
) {
    while(rs.next()) {

    }
    while(stmt.next()) {
        
    }
    while(conn.next()) {
        
    }
} catch(SQLException e) {
    //예외처리
}

▲ try 블록이 종료될 때 객체가 자동으로 닫힘. finally 블록 없이 안전하게 리소스 해제가 가능함!

 

 

 

 

예외 처리

java.sql.SQLException 클래스 혹은 하위 클래스로 예외들이 대부분 처리됨

 

getMessage()

>> 오류 메세지 반환

getSQLState()

>> SQL 상태코드 반환

getErrorCode()

>> 벤더별 에러코드 반환

try {
    //JDBC 작업
} catch (SQLException e) {
    System.err.println("SQL State : " + e.getSQLState());
    System.err.println("Error Code : " + e.getErrorCode());
    System.err.println("Message : " + e.getMessage());

    Throwable t = ex.getCause();
    while(t != null) {
        System.out.println("Cause : " + t);
        t = t.getCause();
    }
}

 

 

 

추가사항

▶ Connection Pooling  커넥션 풀링

데이터베이스 연결은 생성하고 종료하는 상당한 시간과 리소스가 소모됨

커넥션 풀링은 이러한 부담을 줄이기 위해 미리 여러 개의 데이터베이스 연결을 생성해두고 필요할때 제공하고

사용 후에는 다시 풀로 반환하는 방식으로 사용됨

이를 통해 어플리케이션 성능, 확장성이 크게 향상됨

대표적인 커넥션 풀 라이브러리는 HikariCP,  Apache DBCP 등이 있음

 

Transaction Management  트랜잭션 관리

JDBC는 Connection 객체를 통해 데이터 트랜잭션을 관리할 수 있음

setAutoCommit(false)를 호출하여 자동 커밋 기능을 해제하고

commit() 메서드와 rollback() 메서드를 사용하여 직접 커밋 혹은 롤백을 수행할 수 있음

try {
    conn.setAutoCommit(false);
    conn.coommit();
} catch (SQLException e) {
    if (conn != null) {
        try {
            conn.rollback();
        } catch (SQLException e2) {}
    }
}

 

▶ Batch Processing  배치 처리

한번에 많은 양의 SQL 쿼리를 실행해야 할 경우

배치처리 기능을 사용하여 성능을 크게 개선할 수 있음

Statement나 PreparedStatement 객체에서 addBatch() 메서드로 SQL 문장들을 배치에 추가하고,

excuteBatch() 메서드로 한번에 모든 문장들을 실행함

try {
    conn.setAutoCommit(false);
    Statement stmt = conn.createStatement();

    stmt.addBatch("INSERT INTO employees VALUES (1, 'ADAM')");
    stmt.addBatch("INSERT INTO employees VALUES (2, 'Ben')");

    int[] updateCounts = stmt.executeBatch();
} catch (SQLException e) {}

 

 

 


 

 

package myjdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JDBCTest2 {
	public static void main(String[] args) throws Exception {
		Class.forName("com.mysql.cj.jdbc.Driver");
		Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/hr" , "username", "password");
		Statement statement = connection.createStatement();
		String sql = "select * from employees";
		ResultSet rs = statement.executeQuery(sql);
		while (rs.next()) {
			System.out.println(rs.getString("last_name"));
		}
	}
}