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형으로 처리된 행의 갯수를 반환한다
- SELECT문일 경우
[ 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 처리가 필요하다
댓글남기기