JDBC_01) JDBC 데이터베이스 연결과 기본 CRUD 작업
2025. 8. 26. 16:25
  • JDBC(Java Database Connectivity)의 기본 개념 이해
  • Oracle 데이터베이스 연결 방법 학습
  • Statement와 PreparedStatement의 차이점 파악
  • 기본 CRUD(Create, Read, Update, Delete) 작업 구현

리 컴파일된 SQL문을 실행하는 객체 (성능 향상, SQL Injection 방지)

  • ResultSet: SELECT문 실행 후 조회된 결과를 담는 객체

환경 설정

1. JDBC 드라이버 다운로드 및 설정

드라이버 다운로드

자바 프로젝트에 추가

  1. 프로젝트 우클릭 → Build PathAdd External Archives...
  2. 다운로드한 ojdbc17.jar 선택 → 추가
  3. Referenced Libraries에 ojdbc17.jar가 보이면 성공

2. 프로젝트 설정

  • 자동 생성된 Module 삭제 (JDBC 사용을 위해 필요)

3. 데이터베이스 연결 정보

  • URL: jdbc:oracle:thin:@localhost:1521:xe
  • 계정: C##JDBC
  • 비밀번호: JDBC

참고: 127.0.0.1 또는 localhost는 현재 실행중인 컴퓨터의 IP를 의미한다.


SQL - TABLE 미리 생성

- 계정 새로 생성

-- 사용자 계정 생성
CREATE USER C##JDBC IDENTIFIED BY JDBC;
-- 접속 권한 및 테이블 생성 권한 부여
GRANT CONNECT, RESOURCE TO C##JDBC;
-- 테이블스페이스 설정
ALTER USER C##JDBC DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS;

 

TEST 테이블 작성.

CREATE TABLE TEST(
    TNO NUMBER PRIMARY KEY,
    TNAME VARCHAR2(100) NOT NULL,
    TDATE DATE DEFAULT SYSDATE NOT NULL
);

빈 테이블 생성 완료

 

이후 Eclipse 다시 사용

    <INSERT>  (Statement 사용)

public static void insertWithStatement() {
    Connection conn = null;
    Statement stmt = null;
    Scanner sc = new Scanner(System.in);
    int result = 0;

    // 사용자 입력 받기
    System.out.print("번호 : ");
    int tno = sc.nextInt();
    System.out.print("이름 : ");
    String tName = sc.next();

    // SQL문 작성 (주의: SQL Injection 위험)
    String sql = "INSERT INTO TEST VALUES(" + tno + ", '" + tName + "', SYSDATE)";

    try {
        // 1) JDBC 드라이버 등록
        Class.forName("oracle.jdbc.driver.OracleDriver");
        
        // 2) Connection 생성
        conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "C##JDBC", "JDBC");
        conn.setAutoCommit(false);  // 자동커밋 해제
        
        // 3) Statement 생성
        stmt = conn.createStatement();
        
        // 4) SQL문 실행
        result = stmt.executeUpdate(sql);
        
        // 5) 트랜잭션 처리
        if(result > 0) {
            conn.commit();    // 성공시 커밋
        } else {
            conn.rollback();  // 실패시 롤백
        }
        
    } catch (SQLException | ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        // 6) 자원 반납 (생성의 역순)
        try {
            if(stmt != null) stmt.close();
            if(conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

실행 결과:

번호 : 1
이름 : 홍길동
OracleDriver 등록성공
데이터 추가 성공

Oracle SQL DB에도 잘 들어갔다.

 

 

    <SELECT>   (ResultSet 활용)

3개의 데이터 저장 후 실행

 

public static void selectData() {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rset = null;
    List<Test> list = new ArrayList<>();
    
    String sql = "SELECT * FROM TEST";
    
    try {
        // 1) 드라이버 등록
        Class.forName("oracle.jdbc.driver.OracleDriver");
        
        // 2) Connection 생성
        conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "C##JDBC", "JDBC");
        
        // 3) Statement 생성
        stmt = conn.createStatement();
        
        // 4) SQL 실행 (SELECT는 executeQuery 사용)
        rset = stmt.executeQuery(sql);
        
        // 5) ResultSet에서 데이터 추출
        while(rset.next()) {  // 다음 행이 있는지 확인하고 이동
            int tno = rset.getInt("TNO");
            String tName = rset.getString("TNAME");
            Date tDate = rset.getDate("TDATE");
            
            list.add(new Test(tno, tName, tDate.toLocalDate()));
        }
        
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 자원 반납 (생성의 역순)
        try {
            if(rset != null) rset.close();
            if(stmt != null) stmt.close();
            if(conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    // 결과 출력
    if(list.isEmpty()) {
        System.out.println("데이터가 없습니다.");
    } else {
        System.out.println(list);
    }
}

 

결과

 

 

    <UPDATE> (PreparedStatement 사용) 

public static void updateWithPreparedStatement() {
    Scanner sc = new Scanner(System.in);
    Connection conn = null;
    PreparedStatement pstmt = null;
    int result = 0;
   
    // 사용자 입력
    System.out.print("수정할 TNO를 입력 : ");
    int tno = sc.nextInt();
    System.out.print("새로운 이름을 입력 : ");
    String newName = sc.next();
    System.out.print("새로운 날짜를 입력(YYYY-MM-DD) : ");
    String newDate = sc.next();
    
    // PreparedStatement용 SQL문 (? 플레이스홀더 사용)
    String sql = "UPDATE TEST SET TNAME=?, TDATE=TO_DATE(?,'YYYY-MM-DD') WHERE TNO=?";
    
    try {
        Class.forName("oracle.jdbc.driver.OracleDriver");
        
        conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "C##JDBC", "JDBC");
        conn.setAutoCommit(false);
        
        // 미완성된 SQL문으로 PreparedStatement 생성
        pstmt = conn.prepareStatement(sql);
        
        // ? 플레이스홀더에 값 설정 (1부터 시작)
        pstmt.setString(1, newName);  // 첫 번째 ?
        pstmt.setString(2, newDate);  // 두 번째 ?
        pstmt.setInt(3, tno);         // 세 번째 ?
        
        // SQL 실행
        result = pstmt.executeUpdate();
        
        // 트랜잭션 처리
        if(result > 0) {
            conn.commit();
        } else {
            conn.rollback();
        }
        
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if(pstmt != null) pstmt.close();
            if(conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    sc.close();
    
    if(result > 0) {
        System.out.println(result + "개의 행 UPDATE");
    } else {
        System.out.println("UPDATE 실패");
    }
}

실행 결과:

 

DB 반영 결과


Statement vs PreparedStatement 비교

구분 Statement PreparedStatement

SQL 작성 완성된 SQL문 전달 ? 플레이스홀더 사용
성능 매번 컴파일 미리 컴파일되어 재사용
보안 SQL Injection 위험 SQL Injection 방지
사용법 stmt.executeUpdate(sql) pstmt.setString(1, value)
권장도 ❌ 귀찮고 번거로움 ✅ ? 개수만 잘 맞추면 편해짐

 


JDBC 작업 단계 (공통 패턴)

전체 프로세스 흐름도

1. 드라이버 등록 → 2. Connection 생성 → 3. Statement 생성 
→ 4. SQL 실행 → 5. 트랜잭션 처리 → 6. 자원 반납

상세 단계별 설명

1단계: 드라이버 등록

Class.forName("oracle.jdbc.driver.OracleDriver");
  • 목적: JDBC 드라이버 클래스를 JVM에 로드
  • 참고: 최신 JDBC에서는 생략 가능하지만 명시적으로 작성하는 것을 권장

2단계: Connection 생성

conn = DriverManager.getConnection(
    "jdbc:oracle:thin:@localhost:1521:xe", "C##JDBC", "JDBC");
conn.setAutoCommit(false);  // 수동 트랜잭션 설정
  • 목적: 데이터베이스와의 연결 통로 생성
  • AutoCommit 해제: 트랜잭션을 수동으로 관리하기 위함

3단계: Statement/PreparedStatement 생성

// Statement 방식
Statement stmt = conn.createStatement();

// PreparedStatement 방식 (권장)
PreparedStatement pstmt = conn.prepareStatement(sql);

4단계: SQL 실행

  • INSERT/UPDATE/DELETE: executeUpdate() → int 반환 (영향받은 행 수)
  • SELECT: executeQuery() → ResultSet 반환 (조회 결과)

5단계: 트랜잭션 처리

if(result > 0) {
    conn.commit();    // 성공시 커밋
} else {
    conn.rollback();  // 실패시 롤백
}

6단계: 자원 반납

// 생성의 역순으로 반납
if(rset != null) rset.close();      // ResultSet (있는 경우)
if(stmt != null) stmt.close();      // Statement
if(conn != null) conn.close();      // Connection
  • 중요: null 체크 후 close() 호출
  • 순서: 생성의 역순으로 반납해야 함

주의사항

1. 자원 관리

  • 반드시 finally 블록에서 자원 반납
  • null 체크 후 close() 호출
  • 생성의 역순으로 반납: ResultSet → Statement → Connection
finally {
    try {
        if(rset != null) rset.close();
        if(stmt != null) stmt.close(); 
        if(conn != null) conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

2. 트랜잭션 처리

  • setAutoCommit(false) 설정으로 수동 트랜잭션 관리
  • 성공 시 commit(), 실패 시 rollback() 호출
  • 원자성(Atomicity) 보장: 모든 작업이 성공하거나 모두 실패

3. SQL Developer와의 연동 이슈

  • 중요: SQL Developer에서 INSERT한 데이터는 COMMIT 완료 후 Eclipse에서 확인 가능
INSERT INTO TEST VALUES(4, '홍길동', SYSDATE);

잘 들어갔는데?
Update.java에서는 실패한다.
sql에서 커밋 해본다면?
커밋 이후에는 성공한다.

 

>>>  트랜잭션이 완료되지 않으면 다른 세션에서 데이터를 볼 수 없다!

 

4. PreparedStatement 사용 권장 이유

SQL Injection 방지

// 위험한 Statement 방식
String sql = "SELECT * FROM USER WHERE ID='" + userId + "'";
// 만약 userId에 "admin'; DROP TABLE USER; --"가 입력되면?

// 안전한 PreparedStatement 방식  
String sql = "SELECT * FROM USER WHERE ID=?";
pstmt.setString(1, userId);  // 자동으로 이스케이프 처리

성능 향상

  • SQL문을 미리 컴파일해두고 값만 변경하여 재사용
  • 같은 구조의 SQL을 반복 실행할 때 성능상 이점

5. 예외 처리 패턴

try {
    // JDBC 작업 수행
} catch (ClassNotFoundException e) {
    // 드라이버 클래스를 찾을 수 없을 때
    System.out.println("JDBC 드라이버를 확인하세요.");
} catch (SQLException e) {
    // SQL 실행 중 오류 발생
    System.out.println("데이터베이스 연결 또는 SQL 실행 오류");
    e.printStackTrace();
} finally {
    // 자원 반납
}

 

 

  1. PreparedStatement 사용 권장 - 성능과 보안 향상
  2. 자원 관리의 중요성 - 메모리 누수 방지
  3. 트랜잭션 처리 - 데이터 일관성 보장
  4. 예외 처리 - 안정적인 프로그램 작성

 

'JDBC' 카테고리의 다른 글

[JAVA_JDBC] 프로그램 제작  (2) 2025.09.05
JDBC_02) MVC 패턴  (5) 2025.08.27