자바에서 데이터 베이스에 접근하게 해주는 프로그래밍 API이다. 어플리케이션이 서버(DB)와 접속, 응답하며 기능이 이루어지는데 다양한 DBMS와 연동되어 사용할 수 있도록 자바에서 JDBC Iterface를 지정해놨다.
JDBC Interface는 데이터베이스를 이용하는데 필요한 메소드들을 추상화 시켜서 자바에서 지정해준 틀이라고 볼 수 있다. 인터페이스에 대한 구현은 각 회사에서 해놨기 때문에 사용하는 DBMS에 맞춰서 파일을 다운 받아 사용하면 된다.
오라클에서 제공하는 ojdbc.jar 파일을 버전에 맞춰서 다운로드한다. 이클립스에서 프로젝트를 생성하고 생성된 프로젝트 라이브러리에 다운받은 jar파일을 추가한다.
만약 프로젝트 생성 시 라이브러리를 추가하지 못했다면 프로젝트를 클릭한 뒤 마우스 우클릭 해서 build path>Configure build path 들어가서 라이브러리를 추가할 수 있다.
💡라이브러리는 각 프로젝트를 생성할 때 마다 추가해줘야 한다.
해당 창이 뜨면 상단에 라이브러리를 선택해서 Add Externl JARs… 버튼을 누르고 jar파일이 있는 경로를 찾아 Classpath에 추가하면 된다.
이 때, 작업하던 프로젝트를 다른 컴퓨터에 프로젝트를 옮겨서 작업하느라 jdbc 파일의 경로가 달라지면 오류가 발생하므로 라이브러리에서 파일을 Remove로 삭제한 뒤 다시 등록하면 된다.
문자 인코딩 방식이 맞지 않으면 해당 문자가 제대로 출력되지 않기 때문에 Character Set을 UTF-8로 일치 시키는 작업이 필요하다.
상단 Window메뉴에서 Preferences에 들어가서 설정할 수 있다.
General>Workspace에서 Test file encoding 설정을 UTF-8로 변경 후 적용한다.
General>Editors>Text Editors>Spelling에 들어가 Encoding 설정을 UTF-8로 변경하고 적용한다.
아래 JSON, Web>CSS Files, HTML Files, JSP Files도 수정해준다.
위 설정이 끝났다면 JDBC를 사용할 객체를 생성해야 한다. 가장 먼저 Connection 객체가 만들어져야 하고 Connection 객체를 만들기 위해 DriverManager를 사용한다.
DriverManager→Connection→Statement→(필요에 따라)ResultSet 순서로 호출한다.
Class.forName() 메소드 안에 작성한 문장은 oracle을 사용할 때 고정적으로 쓰는 문장으로 외워서 사용하면 된다. 해당 메소드를 선언하면 JVM에게 OracleDriver를 바로 실행할 준비를 하라는 명령을 내린 것으로 이해하면 된다. 예외 처리가 필수인 구문으로 try~catch문을 이용해서 ClassNotFoundException에 대한 처리를 필수로 해줘야 한다.
데이터 원본에 JDBC 드라이버를 통해서 커넥션을 만드는 역할을 한다. 반드시 예외 처리가 필요한 인터페이스로 직접 객체 생성이 불가능 하고 getConnection()
메소드를 사용해서 Connection을 반환하여 객체를 생성할 수 있다.
Connection을 반환하기 때문에 아래 구문처럼 해당 메소드의 반환 값을 Connection객체에 저장하고, 메소드 소괄호안에는 “jdbc : oracle : thin : @IP주소 : 포트번호 : 버전정보”,”계정명”,”비밀번호”를 작성한다.
Ex)
Connection명=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","testname","testpwd");
특정 데이터 원본과 연결된 커넥션을 나타내며 접속된 섹션에 대해 관리하기 때문에 commit
, rollback
을 포함하고 있다. 또한 Statement 객체를 생성할 때도 해당 객체를 사용해서 createStatement()
메소드를 호출하여 생성할 수 있다.
SQL문을 실행하는 역할을 하며 ResultSet 클래스를 포함하고 있다. Connection에 의해서 생성되며 Statement 객체로 질의 문장을 String에 담아서 전달하여 executeQuery()
메소드를 실행하여 SQL을 실행한다.
반환된 SELECT문의 실행결과는 ResultSet 객체가 제공하는 메소드를 이용해서 컬럼별 값을 가져온다.
next()
: 데이터의 row를 지정해서 가져온다. iterator와 동일한 방법으로 다음 Row 데이터를 가져오고 반환형으로 boolean 자료형을 갖는다. iterator에 사용했을 때와 마찬가지로 한 번 불러오면 재사용할 수 없다.
여기서 재사용이 불가능하다는 말은 3번째 데이터를 불러와놓고 다시 1번째 데이터를 불러오는 것이 불가능하다는 것이다. 순서대로 불러온 데이터를 이용하기 위해 객체에 저장해서 사용한다.
get자료형("컬럼명"||인덱스번호)
: 자료형은 String, Int, Date을 자주 사용하며 각 컬럼의 자료형에 맞춰서 사용한다.
package com.myjdbc.controller;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.myjdbc.medel.vo.Department;
public class JdbcTest{
//BS계정으로 접속해 Department 테이블의 정보를 불러오는 메소드
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
//JVM에 OracleDriver가 실행될 수 있도록 한다.
Class.forName("oracle.jdbc.driver.OracleDriver");
//불러올 DB의 정보를 적어서 Connection 객체 반환
conn=DriverManager.getConnection("jdbc:oracle:thin"
+ ":@localhost:1521:xe","BS","BS");
//SQL문을 실행할 수 있도록 Statement 생성
stmt=conn.createStatement();
//SQL문을 문자열로 저장
String sql="SELECT * FROM DEPARTMENT";
//작성한 쿼리로 ResultSet을 반환
rs=stmt.executeQuery(sql);
//반환받은 ResultSet의 Row를 하나씩 List에 저장
List<Department> department=new ArrayList();
while(rs.next()) { //다음 Row가 없을 때까지 반복
department.add(new Department(rs.getString("dept_id")
,rs.getString("dept_title"),rs.getString("location_id")));
//List의 객체를 하나씩 출력하는 forEach문
department.forEach((d)->System.out.println(d));
}
//예외 처리
}catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
}finally { //생성한 객체는 반드시 닫아줘야한다.
if(conn!=null)
try {
if(conn!=null) conn.close();
if(stmt!=null) stmt.close();
if(rs!=null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
💡 각 컬럼을 불러올 때 해당 컬럼에 순서대로 인덱스 번호를 부여한다. 인덱스 번호는 쿼리문을 실행하면서 가져오는 것이기 때문에 DB의 기준대로 1부터 시작한다. while문 안에 아래의 구문을 작성해도 각 컬럼의 값을 하나씩 불러와 Department 객체로 만들어 저장할 수 있다.
department.add(new Department(rs.getString(1),rs.getString(2),rs.getString(3)));
하지만 위 코드처럼 인덱스 번호로 불러오는 경우 해당 컬럼을 명시해주지 않기 때문에 가독성이 떨어진다.
insert, update, delete을 사용하는 쿼리문을 사용할 땐 트랜잭션 처리를 해줘야 한다. 트랜잭션은 기본적으로 connection에서 관리한다.
DML구문이 있는 SQL문 작성은 리터럴 형태에 맞춰서 작성해야 하며 한 번에 하나의 SQL문만 작성해야 한다. 문자열 안에 SQL문을 끝맺는 세미콜론(;)을 작성하지 않기 때문에 여러 개의 SQL문을 작성해도 구분해서 실행할 수 없기 때문이다.
select를 사용할 때와 다르게 executeUpdate(”SQL문”)
메소드를 사용해서 DML구문을 실행시킨다. DML구문은 ResultSet을 반환하지 않고 몇 개의 행이 업데이트 됐는지만 구분하기 때문에 해당 메소드는 int형을 반환한다. 반환된 값이 0이면 실행되지 않은 것이된다.
//SQL문 작성 방법 그대로 작성하되 컬럼값의 자료 타입에 맞춰서 작성한다.
String sql1="INSERT INTO MEMBER VALUES('leeji','leepwd','LEEJI','F'"
+",26,'leeji@lee.com','01012341234','서울',"
+ "'영화감상,음악감상,코딩',SYSDATE)";
//executeUpdate()의 반환 값을 int형으로 받는다.
int result=stmt.executeUpdate(sql1);
//Commit의 자동 실행 여부를 정하는 메소드
//기본적으로 true상태이기 때문에 false로 값을 줘서 직접 commit하게 한다.
conn.setAutoCommit(false);
//위 구문 없이 dml구문 사용 후 commit하려고 하면 이미 커밋을 실행했다는 예외가 발생한다.
//트랜잭션 구문 처리
if(result>0) conn.commit(); //정상 실행 됐으면 commit
else conn.rollback(); //안 된 경우 rollback 실행
System.out.println(result); //1개의 행이 업데이트 됐기 때문에 1을 반환 받았다.
생성한 객체(Connection, Statement, ResultSet)는 반드시 반환해줘야한다. close()
메소드로 반환할 수 있고, 반환은 생성의 역순으로 한다.
close는 반드시 실행되어야 하기 때문에 finally
안에 작성해주고 finally에 작성하기 위해 해당 변수들은 try문 밖에 선언한다.
close() 는 SQLException에 대한 예외 처리를 throws 받은 메소드로 해당 메소드를 사용한 경우 예외 처리를 해줘야 한다. finally문 안에 try~catch문을 중복 작성할 수 있다.