[JDBC][Oracle-thin] 1. JDBC

작성일     업데이트:

카테고리:

태그:

JDBC (Java DataBase Connectivity)

JDBC란

자바에서 데이터베이스에 접근할 수 있도록 해주는 Programming API
Oracle JDBC드라이버로는 type2 형태의 oci Driver 와 type4 형태의 thin Driver가 제공되고 있다

  • thin
    • 순수하게 자바 패키지(클래스들)만으로 바로 DB와 연결하는 방식이다
    • java.sql 패키지를 import하여 사용하게 된다
    • 범용성이 높으며 어떤 하드웨어 OS에서도 유연하게 DB 연결이 가능하다
    • oci 형태보다는 상대적으로 성능이 떨어진다
  • oci
    • Oracle Call Interface
    • .DLL과 .SO파일과 같이 특정 운영체제 내에서만 돌아가는 Native Module을 통해 DB에 연결한다
    • 오라클 DB에 접속하기 위해서는 SQL*NET이 등 오라클 클라이언트 제품이 머신에 설치되어 있어야 하기 때문에 플랫폼에 종속적이다
    • 성능면에서 thin 형태보다 빠른 성능을 낸다

제목에 [JDBC][Oracle-thin] 과 같이 적힌 포스트의 경우 thin 방식의 JDBC 연결을 기준으로 내용을 구성할 예정이다


JDBC 기본 환경 세팅

ojdbc6.jar 파일을 받아 하드상에 저장해두고 JAVA프로젝트에서 사용하도록 하는 방식이다
[ Maven ] 이곳에서 파일을 찾아서 다운로드 할 수 있다
다운로드 받은 파일은 ojdbc6.jar 파일 그대로 사용하며 추후 찾기 쉽도록 C드라이브 상에 dev 폴더를 생성하여 저장해두면 좋다

eclipse 에서 사용할 시 Java 프로젝트에 ojdbc6.jar 파일을 등록해주어야 하는데 설정 방법은 아래와 같다

Java 프로젝트 우클릭 > properties > Java Build Path > Libraries > Modulepath > add external jars에서 해당 파일 추가


JDBC 사용 객체

  • DriverManager

    • Class.forName("oracle.jdbc.driver.OracleDriver");로 JDBC드라이버를 등록하면 이후 DriverManager를 사용 가능하다
    • DriverManager는 직접 객체생성이 불가능하며 getConnection()메소드를 통하여 Connection 객체를 생성 가능하다
      => Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "계정명", "계정비번");
      => 위에서 @localhost 포트번호 등 연결할 db정보 부분은 각자 다를 수 있으니 sqldeveloper 상 연결할 사용자 계정 정보를 확인하여 입력하면 된다
    • 데이터 원본에 JDBC드라이버를 통하여 커넥션을 만드는 역할을 한다
  • Connection

    • DB와의 연결정보(ip주소, port번호, 계정명, 비밀번호)를 담고있는 객체이다
    • SQL문장을 실행시키기 전에 우선 Connection 객체가 있어야 한다
    • 이 Connection 객체를 통해 (Prepared)Statement 객체를 생성한다
  • (Prepared)Statement

    • 연결된 DB에 SQL문을 String 객체에 담아 전달하고 실행한 후 그 결과를 받아내는 객체이다
    • SQL문을 전달하는 방식의 차이에 따라 Statement 와 PreparedStatement 두가지로 나뉜다
      => Statement : 온전히 완성된 SQL문장을 사용
      => PreparedStatement : 미완성된 SQL문장을 사용하며 이후 완성되지 않은 부분을 채워주는 작업이 필요하다 (SQL문장 중 추후에 등록할 부분을 ? 로 표기해 두는 식)
    • Statement 와 PreparedStatement 모두 Connection을 통해 객체생성이 가능하고 PreparedStatement는 Statement를 상속하는 자식 클래스이다
  • ResultSet

    • 만약 실행한 SQL문이 SELECT문일 경우 조회된 결과들이 담겨있는 객체이다
    • 테이블 형태로 결과를 담고있으며 커서(cursor)로 특정 행 참조 조작이 가능하다
    • ResultSet객체에 담긴 데이터들은 따로 ArrayList로 가공하여 활용하도록 하는 것이 일반적이다


JDBC 처리 순서

[ 1) 필요한 변수 생성 ]

Connection conn = null;
Statement stmt = null;
// PreparedStatement pstmt = null;
ResultSet rset = null;
  • 필요한 변수들을 생성한다
  • Statement 와 PreparedStatement는 결국 수행하는 동작은 비슷하므로 둘 중 하나를 선택하여 사용한다
  • 변수에 바로 생성한 객체를 넣어주지 않고 null값으로 준비시킨다
    => 이후에 해당 객체들을 close() 메소드로 종료시켜, 자원을 반납시키는 과정이 필요함
    => 만약 최초에 제대로 생성이 되지 않았다면 반납할 자원 또한 없기 때문에 없는 자원을 반납한다는 에러가 발생할 상황을 방지하고자 하는 것이다


[ 2) JDBC DRIVER 등록 ]

try {
  Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e1) {
  // TODO Auto-generated catch block
  e1.printStackTrace();
}
  • JDBC 드라이버를 등록한다
  • 예외처리가 필수적이다
  • 오타가 있거나 ojdvc6.jar이 없을 경우 ClassNotFoundException이 발생한다


[ 3) Connection 생성 ]

// Connection conn = DriverManager.getConnection(연결할 db정보, 연결할 계정아이디, 연결할 계정 비번);
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "JDBC", "JDBC");
  • 1번 과정에서 생성해두었던 Connection 변수에 Connection 객체를 담아준다
  • 연결할 db 정보를 기입하며 sqldeveloper 상 연결하고자 하는 사용자 계정 정보를 기입하면 된다


[ 4) (Prepared)Statement 생성 ]

/* 아래의 member는 Member 클래스의 객체로
int userId, String pwd 값을 가지고 있는 예시 클래스이다 */

// Statement 사용시
String sql = "INSERT INTO MEMBER VALUES(SEQ_USERNO.NEXTVAL,"
              + "'" + member.getUserId() + "',"
              + "'" + member.getUserPwd() + "',"
              + "DEFAULT)";
stmt = conn.createStatement();

// PreparedStatement 사용시
String sql = "INSERT INTO MEMBER VALUES(SEQ_USERNO.NEXTVAL, ?, ?, DEFAULT)";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, m.getUserId());
pstmt.setString(2 , m.getUserPwd());
  • 생성해두었던 변수에 사용하려는 Statement 객체를 넣어준다
  • Statement에서는 객체 생성시 바로 sql문을 넣어줄 필요는 없지만 PreparedStatement에서 사용되는 구문과의 차이를 보기 쉽게할 수 있도록 이곳에 같이 표기해두었다
  • Statement에서는 중간에 내가 원하는 값을 넣고자 한다면 +로 문자열을 이어주어서 완벽한 String 형태로 구문을 완성시켜서 사용해야 한다
  • PreparedStatement의 경우 객체를 생성하면서 바로 넣어줄 sql문이 필요하다
  • PreparedStatement 에서는 미완성된 sql문도 사용가능하기 때문에 추후 넣어줄 값이 들어갈 위치에 ?만 표기해두면 된다 (완성된 sql문도 사용가능하다)
  • 이후 미완성된 sql문엔 pstmt.setXXX( ?의 위치, 실제 값 );를 사용하여 값을 넣어줄 수 있다
    => 넣어줄 값의 자료형에 따라 setInt(), setString() 등으로 맞춰서 사용하면 된다
    => ?의 위치값은 1부터 시작하니 주의가 필요하다


[ 5) SQL문의 실행 및 결과 받기 ]

// SELECT문일 경우
/*ResultSet*/ rset = /*(Prepared)Statement*/stmt.executeQuery(sql);

// 그 외 DML문일 경우
/*int*/ result = stmt.executeUpdate(sql);
  • 윗단계들에서 만들어둔 (Prepared)Statement 객체에 excuteXXX()를 이용하여 DB 상 sql문을 실행시킨 후 결과값을 받아온다
  • Statement 객체일 경우 먼저 String 형으로 만들어두었던 sql문을 excuteXXX(sql)식으로 넣어주어야 한다
  • PreparedStatement 객체일 경우 객체 자체에 sql문을 넣어두었기 때문에 따로 또 넣어줄 필요 없이 excuteXXX()만 사용하면 된다
  • excuteXXX()
    • SELECT문일 경우 executeQuery() : ResultSet 객체로 결과값을 반환한다
    • 그 외 DML문일 경우 executeUpdate() : int형으로 처리된 행의 갯수를 반환한다


[ 6) SQL문 실행결과에 따른 처리 ]

// 결과값이 ResultSet인 경우
ArrayList<ResultClass> list = new ArrayList<>();

while( /*ResultSet*/rset.next() ) {
  //ResultClass는 테스트용 클래스임
  ResultClass rc = new ResultClass();

  rc.setUserNo(rset.getInt("USERNO")); //실제 DB테이블의 컬럼명들을 넣어줌
  rc.setUserId(rset.getString("USERID"));
  rc.setUserPwd(rset.getString("USERPWD"));
  rc.setEnrollDate(rset.getDate("ENROLLDATE"));

  list.add(rc);
}

// 결과값이 int형인 경우
if( /*int*/result > 0 ) {// 1개 이상의 행이 처리되었다면 -> 커밋
  /*Connection*/conn.commit();
}else {// 실패했을 경우 -> 롤백
  conn.rollback();
}
  • 결과값이 ResultSet인 경우
    • 실행했던 sql문이 SELECT인 경우 sql 질의에 의해 생성된 테이블이 ResultSet에 담겨 반환되게 된다
    • ResultSet에서는 커서(cursor)로 특정 행에 대한 참조를 조작할 수 있다
    • 위의 예시에서는 rset.next()로 cursor위치를 한칸씩 아래로 내려, 행이 존재하는지 확인 후 해당 행의 값을 컬럼값(ex. USERNO, USERID…)으로 찾아 불러온 것이다
      => 불러올 값이 속해있는 컬럼명을 사용하여 값을 불러오며 자료형또한 지정해주어야 한다
    • ResultSet으로 불러와진 값들은 일반적으로 한행씩 뽑아서 VO 객체에 넣어준 후 ArrayList로 관리하게 된다
  • 결과값이 int형인 경우
    • 실행했던 sql문이 기타 DML문인 경우 처리된 행의 갯수가 int형으로 반환되게 된다
    • 총 몇행이 처리되었는지를 알 수 있게 되므로 처리된 행의 갯수가 0 이하라면 제대로 sql문이 실행되지 못했거나 변동이 없었다는 뜻이 된다
    • 따라서 일반적으로 처리된 행의 갯수가 1개 이상이라면 commit, 그 외엔 실패로 간주하고 rollback 처리를 해주게 된다
    • commit과 rollback은 Connection 객체를 이용하여 처리한다
  • 이 단계에서 수행결과를 받아 원하는 대로 해당 데이터들을 처리하는 로직을 구성하면 된다


[ 7) 사용이 끝난 JDBC용 객체들을 반납하기 ]

// 1) 사용할 변수 생성 구역
try{
  // 2~6) JDBC용 객체 생성 및 사용 구역
} catch (ClassNotFoundException e) {
  e.printStackTrace();
} catch (SQLException e) {
  e.printStackTrace();
} finally {
  // 7) 다쓴 자원 반납해주기 -> 생성된 순서의 역순으로
  try {
    rset.close();
    stmt.close();
    conn.close();
  } catch (SQLException e) {
    e.printStackTrace();
  }
}
  • finally 구역 안에서 각 객체들을 닫아준다
  • JDBC객체들이 혹시라도 열린 채 남아있게 되면 계속해서 자원을 낭비하게 되므로 꼭 닫아주기 위해 finally구역에서 닫아주는 것이다
  • 생성된 순서의 역순으로 ResultSet -> (Prepared)Statement -> Connection 순서대로 닫아준다
  • 이때 반드시 SQLException 처리가 필요하다

댓글남기기