JDBC (Java Database Connectivity) API를 사용하는 방법이 궁금하여 정리해보고자 합니다. JDBC는 Java 애플리케이션이 다양한 관계형 데이터베이스와 상호작용할 수 있도록 표준화된 방법을 제공합니다.
JDBC를 사용하여 SQL 쿼리를 실행하려면 다음과 같은 단계를 거칩니다.
Class.forName())데이터베이스와 통신하려면 해당 데이터베이스 벤더가 제공하는 JDBC 드라이버를 JVM에 로드해야 합니다.
try {
Class.forName("com.mysql.cj.jdbc.Driver"); // MySQL 드라이버
// Class.forName("oracle.jdbc.driver.OracleDriver"); // Oracle 드라이버
// Class.forName("org.postgresql.Driver"); // PostgreSQL 드라이버
System.out.println("JDBC 드라이버 로드 성공!");
} catch (ClassNotFoundException e) {
System.err.println("JDBC 드라이버를 찾을 수 없습니다: " + e.getMessage());
e.printStackTrace();
}
Class.forName() 메소드를 사용하여 드라이버 클래스를 메모리에 로드합니다. 이렇게 하면 드라이버가 스스로 등록됩니다. (JDBC 4.0 이상에서는 이 단계가 필수는 아니지만, 명시적으로 로드하는 것이 명확합니다.)DriverManager.getConnection())로드된 드라이버를 사용하여 특정 데이터베이스에 연결합니다.
String url = "jdbc:mysql://localhost:3306/userdb?useSSL=false&serverTimezone=UTC"; // JDBC URL
String user = "root"; // 데이터베이스 사용자 이름
String password = "1234"; // 데이터베이스 비밀번호
Connection conn = null; // Connection 객체 선언
try {
conn = DriverManager.getConnection(url, user, password);
System.out.println("데이터베이스 연결 성공!");
} catch (SQLException e) {
System.err.println("데이터베이스 연결 실패: " + e.getMessage());
e.printStackTrace();
}
url (JDBC URL): 연결할 데이터베이스의 위치와 속성을 지정합니다. jdbc:mysql:// 부분은 MySQL 드라이버를 사용한다는 의미이고, localhost:3306은 데이터베이스 서버의 주소와 포트, /userdb는 데이터베이스 이름입니다. ? 이후는 추가 옵션입니다.user, password: 데이터베이스 접속에 필요한 사용자 이름과 비밀번호입니다.Connection 객체: 데이터베이스 세션을 나타냅니다. 모든 SQL 작업은 이 연결을 통해 이루어집니다.SQL 쿼리를 실행하기 위한 객체를 생성합니다.
Statement (정적 SQL 쿼리용):
Statement stmt = null;
try {
stmt = conn.createStatement();
System.out.println("Statement 객체 생성 성공!");
} catch (SQLException e) {
e.printStackTrace();
}
PreparedStatement (매개변수 있는 동적 SQL 쿼리용 - 권장):
String sqlInsert = "INSERT INTO users (name, email) VALUES (?, ?)"; // '?' 자리표시자
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(sqlInsert);
System.out.println("PreparedStatement 객체 생성 성공!");
} catch (SQLException e) {
e.printStackTrace();
}
?)와 같은 자리표시자가 포함되어 실행 시점에 값을 바인딩해야 하는 쿼리에 사용됩니다. 보안(SQL 인젝션 방지) 및 성능 향상 측면에서 Statement보다 훨씬 권장됩니다.생성된 Statement 또는 PreparedStatement 객체를 사용하여 SQL 쿼리를 실행합니다.
INSERT, UPDATE, DELETE (데이터 변경):
Statement 사용 시:
String sqlUpdate = "UPDATE users SET email = 'new_email@example.com' WHERE name = 'Alice'";
try {
int rowsAffected = stmt.executeUpdate(sqlUpdate);
System.out.println(rowsAffected + " 개의 행이 업데이트되었습니다.");
} catch (SQLException e) {
e.printStackTrace();
}
PreparedStatement 사용 시:
String sqlUpdate = "UPDATE users SET email = ? WHERE name = ?";
try {
pstmt = conn.prepareStatement(sqlUpdate);
pstmt.setString(1, "new_email@example.com"); // 첫 번째 '?'
pstmt.setString(2, "Alice"); // 두 번째 '?'
int rowsAffected = pstmt.executeUpdate();
System.out.println(rowsAffected + " 개의 행이 업데이트되었습니다.");
} catch (SQLException e) {
e.printStackTrace();
}
executeUpdate(): INSERT, UPDATE, DELETE와 같이 데이터베이스의 데이터를 변경하는 SQL 쿼리를 실행할 때 사용합니다. 실행된 행의 수를 int 타입으로 반환합니다.SELECT (데이터 조회):
Statement 사용 시:
String sqlSelect = "SELECT id, name, email FROM users WHERE id > 10";
ResultSet rs = null;
try {
rs = stmt.executeQuery(sqlSelect);
// 5. 결과 처리 부분으로 이어짐
} catch (SQLException e) {
e.printStackTrace();
}
PreparedStatement 사용 시:
String sqlSelect = "SELECT id, name, email FROM users WHERE id > ?";
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(sqlSelect);
pstmt.setInt(1, 10); // 첫 번째 '?'
rs = pstmt.executeQuery();
// 5. 결과 처리 부분으로 이어짐
} catch (SQLException e) {
e.printStackTrace();
}
executeQuery(): SELECT 쿼리를 실행하여 데이터를 조회할 때 사용합니다. 쿼리 결과를 ResultSet 객체로 반환합니다.ResultSet - SELECT 문인 경우)SELECT 쿼리의 결과를 ResultSet 객체로부터 읽어들입니다.
// 위에서 얻은 ResultSet rs 객체를 사용
if (rs != null) { // rs가 null이 아닌지 확인 (쿼리 실패 방지)
try {
while (rs.next()) { // 다음 행이 존재하는 동안 반복
int id = rs.getInt("id"); // "id" 컬럼의 정수 값 가져오기
String name = rs.getString("name"); // "name" 컬럼의 문자열 값 가져오기
String email = rs.getString("email"); // "email" 컬럼의 문자열 값 가져오기
// 컬럼 인덱스로도 가져올 수 있습니다. (성능은 약간 좋지만 가독성은 떨어짐)
// int id = rs.getInt(1); // 첫 번째 컬럼
// String name = rs.getString(2); // 두 번째 컬럼
System.out.printf("ID: %d, 이름: %s, 이메일: %s\n", id, name, email);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
rs.next(): ResultSet의 커서를 다음 행으로 이동시키고, 다음 행이 존재하면 true, 없으면 false를 반환합니다. while 루프와 함께 사용하여 모든 행을 순회합니다.rs.get데이터타입("컬럼이름") 또는 rs.get데이터타입(컬럼인덱스): 현재 커서가 가리키는 행에서 지정된 컬럼의 값을 Java 타입으로 가져옵니다.데이터베이스 자원은 사용 후 반드시 닫아주어 누수를 방지해야 합니다. try-with-resources 문을 사용하는 것이 가장 권장되는 방법입니다.
// try-with-resources 사용 (Java 7 이상)
// 괄호 안에 선언된 자원들은 블록이 끝나면 자동으로 close() 메소드가 호출됩니다.
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement(); // 또는 PreparedStatement
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) { // SELECT 쿼리인 경우
// SQL 쿼리 실행 및 결과 처리 코드
} catch (SQLException e) {
System.err.println("SQL 에러 발생: " + e.getMessage());
e.printStackTrace();
} catch (ClassNotFoundException e) { // Class.forName() 에서 발생 가능
System.err.println("드라이버 로드 에러: " + e.getMessage());
e.printStackTrace();
}
try-with-resources: AutoCloseable 인터페이스를 구현하는 객체(여기서는 Connection, Statement, ResultSet)를 try 문 괄호 안에 선언하면, try 블록이 정상적으로 종료되거나 예외가 발생하더라도 해당 객체들의 close() 메소드가 자동으로 호출됩니다. 이 방법이 자원 관리의 가장 안전하고 효율적인 방법입니다.import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement; // 또는 java.sql.Statement
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcSelectExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/userdb?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "1234";
// try-with-resources를 사용하여 자원 자동 해제
try (Connection conn = DriverManager.getConnection(url, user, password);
// PreparedStatement 사용 예시 (더 권장)
PreparedStatement pstmt = conn.prepareStatement("SELECT id, name, email FROM users WHERE id > ?")) {
// 드라이버 로드는 JDBC 4.0+ 에서는 필수가 아니지만, 명시적으로 넣어줄 수 있습니다.
// Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("데이터베이스 연결 성공!");
// PreparedStatement에 값 바인딩
pstmt.setInt(1, 0); // id가 0보다 큰 모든 사용자 (WHERE id > 0)
// 쿼리 실행 및 결과 받기
ResultSet rs = pstmt.executeQuery();
System.out.println("📝 저장된 사용자 목록:");
// ResultSet에서 데이터 한 행씩 읽기
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.printf("👉 [%d] %s / %s\n", id, name, email);
}
} catch (SQLException e) {
System.err.println("SQL 에러 발생: " + e.getMessage());
e.printStackTrace();
}
// ClassNotFoundException은 DriverManager.getConnection() 호출시 드라이버 로드가
// 실패했을 때 발생할 수 있지만, 요즘은 대부분의 경우 자동으로 로드되므로 생략하기도 합니다.
// 하지만 Class.forName()을 명시적으로 사용한다면 필요합니다.
/* catch (ClassNotFoundException e) {
System.err.println("JDBC 드라이버를 찾을 수 없습니다: " + e.getMessage());
e.printStackTrace();
}*/
}
}
이것이 SQL과 관련된 Java (JDBC) 코드의 기본적인 문법과 흐름입니다. 실제 프로젝트에서는 Connection Pool, ORM (JPA/Hibernate, MyBatis) 등을 사용하여 JDBC 코드를 직접 작성하는 것을 최소화하기도 하지만, JDBC의 기본 원리를 이해하는 것은 매우 중요합니다.