본문 바로가기

학원/복기

[JDBC] JDBC란? / JDBC 설치 / JDBC 프로그램 작성법

JDBC(Java Database Connectivity)

: Java를 사용하여 DBMS 서버에 접속해 SQL 명령을 전달하여 실행하기 위한 기능을 제공하는 API(클래스 또는 인터페이스) 

JDBC는 자바 애플리케이션과 데이터베이스 간의 연결을 제공하는 자바 API(응용 프로그래밍 인터페이스)이다. JDBC를 사용하여 자바 애플리케이션에서 다양한 데이터베이스에 접속하고 SQL 쿼리를 실행할 수 있다.

java.sql : JDBC 기능의 프로그램을 작성하기 위한 클래스 및 인터페이스가 선언된 패키지 
→ java.sql 패키지에서는 JDBC 기능 구현을 위해  인터페이스를 제공한다. (DBMS 종류가 다양하므로 클래스로 제공이 불가능하다.)

DBMS 프로그램을 관리하는 회사에서 JDBC 기능을 구현하기 위한 클래스(JDBC Driver)를 배포하여 제공한다. 
→ JDBC Driver가 포함된 라이브러리 파일(Jar 파일)을 다운로드 받아 프로젝트 빌드 처리 해야만 라이브러리의 클래스를 사용하여 JDBC 프로그램 작성이 가능하다. 

Oracle DBMS를 이용해 JDBC 프로그램을 작성하기 위한 환경설정

  1. https://www.oracle.com/ 사이트에서 Oracle JDBC Driver 관련 라이브러리 파일을 다운로드
    → Oracle JDBC Driver : ojdbc11.jar (JDK 버전 참고해 해당하는 jdbc버전을 다운로드 받으면 된다.)
  2.  Oracle JDBC Driver 관련 라이브러리 파일(ojdbc11.jar)을 프로젝트 폴더에 붙여넣기
  3.  프로젝트 폴더에 저장된 라이브러리 파일을 프로젝트에서 사용할 수 있도록 연결 (빌드(Build) 처리)
    → 라이브러리 파일(Jar)에 클래스 및 인터페이스를 프로젝트에서 사용 가능하도록 설정 
    → 프로젝트 >> 마우스 우클릭 >> Properties(속성) >> Java Build Path(메뉴) >> Libraries(탭메뉴) >> classpath(클릭) >> Add Jars(선택)  >> 프로젝트 내부의 Jar(ojdbc11.jar) 선택 >> Apply and Close 


Class 객체(Clazz)를 제공받는 방법

1. Class.forName(String className) 메소드를 호출하여 반환받는 방법
2. Object.getClass() 메소드를 호출하여 현재 사용중인 클래스에 대한 Class 객체 반환받는 방법
3. [클래스명.class] 형식으로 표현하는 방법

JDBC는 기본적으로 Class.forName(String className) 형태로 객체를 생성해 사용한다. 
수동으로 객체를 생성하는 이유 : 
불필요한 객체를 생성하거나, 메소드를 호출하는 것을 방지하기 위해서 정적영역 내에 객체와 메소드를 생한다. (정적영역 내에 작성된 명령은 프로그램 실행시 한번만 실행되기 때문이다) 

 

Class.forName(String className) 

  • : 문자열로 표현된 패키지가 포함된 클래스를 전달받아 ClassLoader 프로그램을 사용하여 클래스를 읽어 메모리에 저장하는 메소드
  • -> 메모리에 저장된 클래스의 정보가 저장된 Class 객체(Clazz) 반환
  • -> ClassNotFoundException 발생 : 해당  패키지에 클래스가 없는 경우 발생 - 일반 예외
//StaticBlock 클래스 선언

public class StaticBlock {
	//정적영역(Static Block) : 클래스가 메모리에 저장된 후 자동 실행될 명령을 작성하는 영역
	// => 정적영역에 작성된 명령은 프로그램 실행시 한번만 실행
	static {
			System.out.println("### StaticBlock 클래스의 정적영역에 작성된 명령 실행 ###");
			StaticBlock sb=new StaticBlock();
			sb.display();
	}
	public StaticBlock() {
		System.out.println("### StaticBlock 클래스의 기본 생성자 호출 - 객체 생성 ###");
	}
	
	public void display() {
		System.out.println("### StaticBlock 클래스의 display() 메소드 호출 ###");
	}
}

//StaticBlockApp 클래스 선언 

public class StaticBlockApp {
	public static void main(String[] args) throws ClassNotFoundException {
		Class.forName("xyz.itwill.jdbc.StaticBlock");//수동으로 클래스를 메모리에 저장
		
	}
}

 

StaticBlockApp 컴파일 후 실행 

 


JDBC 프로그램 작성법

1. OracleDriver 클래스로 객체를 생성하여 DriverManager 클래스의 JDBC Driver 객체로 등록 Class.forName("oracle.jdbc.driver.OracleDriver");

2. DriverManager 클래스에 등록된 JDBC Driver 객체를 이용하여 DBMS 서버에 접속하여 Connection 객체를 반환받아 저장        DriverManager.getConnection(String url, String user, String password)
      ** Oracle DBMS 서버에 접속하여 데이터베이스에 접근하기 위한 URL 주소  
           형식) jdbc:oracle:thin:@ServerName:Port:SID

3. Connection 객체로부터 SQL 명령을 전달할 수 있는 Statement 객체를 반환받아 저장 

4. Statement 객체를 사용하여 SQL 명령(INSERT,UPDATE,DELETE,SELECT)을 DBMS 서버에 전달하여 실행하고 실행결과를 반환받아 저장

5. 반환받은 SQL 명령의 실행 결과값을 사용자에게 제공 (출력)

6. JDBC 관련 객체를 모두 제거 (객체가 생성된 순서의 반대로 제거해야 한다.)  

 

 

예시) STUDENT 테이블에 학생정보를 삽입하는 JDBC 프로그램 작성 

 

SQL 디벨로퍼에서 SCOTT 사용자를 이용해 STUDENT 테이블 생성

STUDENT 테이블 - 학번(숫자형-PRIMARY KEY),이름(문자형),전화번호(문자형),주소(문자형),생년월일(날짜형)

CREATE TABLE STUDENT(NO NUMBER(4) PRIMARY KEY, NAME VARCHAR2(50), PHONE VARCHAR2(20),
ADDRESS VARCHAR2(100),BIRTHDAY DATE);


1.OracleDriver 클래스로 객체를 생성하여 DriverManager 클래스의 JDBC Driver 객체로 등록 

- JDBC Driver 객체 : DriverManager 클래스에 등록되어 관리되는 Driver 객체
- Driver 객체 : DBMS 서버에 접속하여 SQL 명령을 전달하는 기능을 제공하는 객체 
- DriverManager 클래스 : Driver 객체를 관리하기 위한 기능을 제공하는 클래스 
- DriverManager.registerDriver(Driver driver) : Driver 객체를 매개변수로 전달받아 DriverManager 클래스가 관리할 수 있는  JDBC Driver 객체로 등록하는 메소드

 

첫번째 방법) - 비권장 

DriverManager.registerDriver(new OracleDriver());

이 방법을 사용했을 때, 동일한 클래스로 생성된 Driver 객체가 DriverManager 클래스에 여러 개 등록될 수 있다.
때문에 불필요한 Driver 객체가 존재할 수 있다는 점이 가장 큰 단점이다. 따라서  해당 방법은 권장하지 않는다.

두번째 방법) - 권장 

Class.forName(String className) 메소드를 호출하여 ClassLoader 프로그램을 이용하여OracleDriver 클래스를 읽어 메모리에 저장하는 방법 

Class.forName("xyz.itwill.jdbc.StaticBlock");

→ OracleDriver 클래스의 정적 영역에서 OracleDriver 클래스를 객체로 생성하여 DriverManager 클래스의 JDBC Driver로 등록하는 메소드를 호출한다.

이 방법의 장점은, 

Class.forName("xyz.itwill.jdbc.StaticBlock");
Class.forName("xyz.itwill.jdbc.StaticBlock");
Class.forName("xyz.itwill.jdbc.StaticBlock");

이처럼 Class.forName을 여러개 사용해도 메모리에 하나밖에 존재하지 않는다는 것이다. 

Class.forName은 여러번 호출해도 메모리에 1번밖에 등록되지 않는다. 따라서 DriverManager 클래스에 Driver 객체가 불필요하게 여러개 등록되는 것을 방지할 수 있다. 

OracleDriver 클래스를 읽어들이면 메모리에 저장하여 자동으로 Oracle 객체를 생성하여  DriverManager 클래스의 JDBC Drive 객체로 등록된다.

2.DriverManager 클래스에 등록된 JDBC Driver 객체를 이용하여 DBMS 서버에 접속 (Connection 객체를 반환받아 저장)

- DriverManager.getConnection(String url, String user, String password)
 : DriverManager 클래스에 등록된 JDBC Driver 객체를 이용하여 DBMS에 접속하는 메소드
→ DBMS 서버에 접속된 정보가 저장된 Connection 객체를 반환한다. 
→ 접속 URL 주소의 프로토콜을 이용하여 특정 DBMS 서버에 접속할 수 있다. 

URL(Uniform Resource Location) : 인터넷에 존재하는 자원의 위치를 표현하는 주소 
형식) Protocol:ServerName:Port:Resource >> http://www.itwill.xyz:80/test/index.html

Oracle DBMS 서버에 접속하여 데이터베이스에 접근하기 위한 URL 주소 
형식) jdbc:oracle:thin:@ServerName:Port:SID

String url="jdbc:oracle:thin:@localhost:1521:xe"; //1521포트에 접속해 xe 데이터베이스 사용
String user="scott"; //사용자 이름
String password="tiger"; //비밀번호

Connection con=DriverManager.getConnection(url, user, password);

 

3. Connection 객체로부터 SQL 명령을 전달할 수 있는 Statement 객체를 반환받아 저장 

- Connection.createStatement() : SQL 명령을 전달할 수 있는 Statement 객체를 생성하여 반환하는 메소드
- Statement 객체 : SQL 명령을 현재 접속중인 DBMS 서버에 전달하기 위한 기능을 제공하는 객체 

Statement stmt=con.createStatement();

 

4. Statement 객체를 사용하여 SQL 명령(INSERT,UPDATE,DELETE,SELECT)을 DBMS 서버에 전달하여 실행하고 실행결과를 반환받아 저장

- Statement.executeUpdate(String sql) : DML 명령(INSERT,UPDATE,DELETE)을 전달하여 실행하는 메소드 
→ DML 명령의 실행결과로 조작행의 갯수를 정수값으로 반환해준다.
- Statement.excuteQuery(Stirng sql) : SELECT 명령을 전달하여 실행하는 메소드 
→ SELECT 명령의 실행결과로 검색행이 저장된 ResultSet 객체를 반환해준다. 

String sql="insert into student values(1000,'홍길동','010-1234-5678','서울시 강남구','00/01/01')"; //행삽입 
int rows=stmt.executeUpdate(sql);

 

5. 반환받은 SQL 명령의 실행 결과값을 사용자에게 제공 (출력)

System.out.println("[메시지]"+rows+"명의 학생정보를 삽입 하였습니다.");

 

접속되어있는 Connection을 끊지 않으면 접속 상태가 계속 유지된다. 따라서 이를 끊어줘야 한다.

6. JDBC 관련 객체를 모두 제거 (객체가 생성된 순서의 반대로 제거해야 한다.)  

Statement.close() : Statement 객체를 삭제하는 메소드 

//Statement.close() : Statement 객체를 삭제하는 메소드
if(stmt!=null) stmt.close();
//Connection.close() : Connection 객체를 삭제하는 메소드 - 접속 종료 
if(con!=null) con.close();

→ NullPointerException이 발생할 수 있으므로 예외 발생을 방지하기 위해 if 구문 사용
NullPointerException : 참조변수에 NULL이 저장된 상태에서 메소드를 호출한 경우 발생하는 예외

 

SQL 디벨로퍼에서 검색

 

전체 소스코드

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class InsertStudentApp {
	public static void main(String[] args) {
		//JDBC 관련 객체를 저장하기 위한 참조변수는 try 영역 외부에서 선언
		// => try 영역을 포함한 모든 영역에서 참조변수를 이용하여 객체 사용 가능 
		Connection con=null;
		Statement stmt=null;
		try {
			//1.OracleDriver 클래스로 객체를 생성하여 DriverManager 클래스의 JDBC Drive 객체로 등록 
			Class.forName("oracle.jdbc.driver.OracleDriver");
			
			//2.DriverManager 클래스에 등록된 JDBC Driver 객체를 이용하여 DBMS 서버에 접속
			//DriverManager.getConnection(String url, String user, String password)
			String url="jdbc:oracle:thin:@localhost:1521:xe"; 
			String user="scott";
			String password="tiger";
			
			con=DriverManager.getConnection(url, user, password);
			
			//3.Connection 객체로부터 SQL 명령을 전달할 수 있는 Statement 객체를 반환받아 저장 
			//Connection.createStatement() : 
			stmt=con.createStatement();
			
			//4.Statement 객체를 사용하여 SQL 명령(INSERT,UPDATE,DELETE,SELECT)을 DBMS 서버에 
			//전달하여 실행하고 실행결과를 반환받아 저장
			//String sql="insert into student values(1000,'홍길동','010-1234-5678','서울시 강남구','00/01/01')";
			//String sql="insert into student values(2000,'임꺽정','010-7894-5612','수원시 월정구','02/05/08')";
			String sql="insert into student values(3000,'전우치','010-1478-2589','인천시 상당구','98/07/12')";
			int rows=stmt.executeUpdate(sql);
			
			//5.반환받은 SQL 명령의 실행 결과값을 사용자에게 제공 (출력)
			System.out.println("[메시지]"+rows+"명의 학생정보를 삽입 하였습니다.");
			
		} catch (ClassNotFoundException e) {
			System.out.println("OracleDriver 클래스를 찾을 수 없습니다.");
		//JDBC 관련 클래스의 메소드를 호출한 경우 반드시 SQLException 발생 - 일반예외 
		} catch (SQLException e) {
			System.out.println("[에러]JDBC 관련 오류 = "+e.getMessage());
		} finally {//예외 발생과 상관없이 무조건 실행되는 명령을 작성하는 영역 
			try {
				//6.JDBC 관련 객체를 모두 제거 - 객체가 생성된 순서의 반대로 제거
				if(stmt!=null) stmt.close();
				if(con!=null) con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 


예시) STUDENT 테이블에 저장된 학생정보 중 학번이 [2000]인 학생의 이름을 [임걱정]으로 변경하고 주소를 [부천시 원미구]로 변경하는 JDBC 프로그램 작성 

 

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class UpdateStudentApp {
	public static void main(String[] args) {
		Connection con=null;
		Statement stmt=null;
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
			
			String url="jdbc:oracle:thin:@localhost:1521:xe";
			String user="scott";
			String password="tiger";
			con=DriverManager.getConnection(url, user, password);
			
			stmt=con.createStatement();
			
			String sql="update student set name='임걱정',address='부천시 원미구' where no=2000";
			int rows=stmt.executeUpdate(sql);
			
			System.out.println("[메세지]"+rows+"명의 학생정보를 변경 하였습니다.");
		} catch (ClassNotFoundException e) {
			System.out.println("[에러]OracleDriver 클래스를 찾을 수 없습니다.");
		} catch (SQLException e) {
			System.out.println("[에러]JDBC 관련 오류 = "+e.getMessage());
		} finally {
			try {
				if(stmt!=null) stmt.close();
				if(con!=null) con.close();
			} catch (SQLException e) { }
		}
	}
}

 


STUDENT 테이블에 저장된 모든 학생정보를 검색하여 출력하는 JDBC 프로그램 작성 

SELECT 명령을 전달할 때는 다른 SQL 명령을 전달할 때와는 조금 다르게 ResultSet 객체를 생성해야 한다.

Statement.executeQuery(String sql) : SELECT 명령을 전달하여 실행하는 메소드 
-> SELECT 명령의 실행 결과(검색행)를 ResultSet 객체에 저장하여 반환 
ResultSet 객체 : 검색결과를 2차원 배열(행과 열)의 형태로 저장하여 제공하는 객체 

ResultSet 객체에 저장된 검색행을 행단위로 처리하기 위해 ResultSet 객체는 내부적인 커서(Cursor)를 제공한다.
-> ResultSet 커서는 최초 ResultSet 객체의 BOF(Before Of File) 영역에 위치한다.

ResultSet.next() : ResultSet 커서를 다음행으로 이동하는 메소드 
-> false 반환 : ResultSet 커서 위치에 처리행이 없는 경우의 반환값 (ResultSet 커서가 EOF(End Of FIle) 영역에 위치)
-> true 반환 : ResultSet 커서 위치에 처리행이 있는 경우의 반환값

BOF / EOF 의 위치

ResultSet.getXXX(int columnIndex) 또는 ResultSet.getXXX(String columnLabel)
-> ResultSet 커서가 위치한 처리행의 컬럼값을 반환하는 메소드 
-> XXX : 컬럼값을 반환받기 위한 Java 자료형을 표현 
-> columnIndex : 검색행에서 검색대상의 순서대로 1부터 1씩 증가되는 정수값을 표현 - 첨자
-> columnLabel : 검색행에서 검색대상의 이름을 문자열로 표현

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SelectStudentApp {
	public static void main(String[] args) {
		Connection con=null;
		Statement stmt=null;
		ResultSet rs=null;
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");	
			
			String url="jdbc:oracle:thin:@localhost:1521:xe";
			String user="scott";
			String password="tiger";
			con=DriverManager.getConnection(url, user, password);
			
			stmt=con.createStatement();
			
			//다중행을 검색할 땐 order by를 사용해 정렬하여 검색하는 것이 좋다. 
			String sql="select * from student order by no";
			
           	 	//SELECT 명령의 실행 결과(검색행)를 ResultSet 객체에 저장하여 반환 
			rs = stmt.executeQuery(sql); //rs에는 검색결과가 저장된다.
			
			if(rs.next()) {//ResultSet 커서 위치에 처리행이 있는 경우
				//검색된 다수의 학생정보가 저장된 ResultSet 객체를 처리하기 위한 반복문 
				// => ResultSet 객체에 저장된 검색행의 갯수가 불확실하므로 while 구문 사용 
				// => if 구문에서 ResultSet 커서를 다음행으로 이미 이동했기 때문에 do~while 구문 사용
				do {
					//int no=rs.getInt(1);
					int no=rs.getInt("no"); //주로 columnLabel을 사용 
					String name=rs.getString("name");
					String phone=rs.getString("phone");
					String address=rs.getString("address");
					//Date birthday=rs.getDate("birthday");
					//처리행의 컬럼값은 오라클 자료형에 상관없이 getString() 메소드를 호출하여
					//문자열(String 객체)로 반환 가능 
					String birthday=rs.getString("birthday");
					
					System.out.println("학번 = "+no);
					System.out.println("이름 = "+name);
					System.out.println("전화번호 = "+phone);
					System.out.println("주소 = "+address);
					//오라클의 날짜값을 문자열로 반환받으면 [yyyy-MM-dd HH:mm:ss] 형식으로 반환
					// => 날짜만 출력되도록 문자열을 분리하여 출력 
					System.out.println("생년월일 = "+birthday.substring(0,10));
					System.out.println("========================================================");
				} while(rs.next());//ResultSet 커서를 다음행으로 이동 - 처리행이 있는 경우 반복문 실행, 처리행이 없는 경우 반복문 종료 
 			} else {//ResultSet 커서 위치에 처리행이 없는 경우
				System.out.println("[메시지]검색된 학생정보가 없습니다.");
			}
		} catch (ClassNotFoundException e) {
			System.out.println("[에러]OracleDriver 클래스를 찾을 수 없습니다.");
		} catch (SQLException e) {
			System.out.println("[에러]JDBC 관련 오류 = "+e.getMessage());
		} finally {
			try {
				if(rs!=null) rs.close();
				if(stmt!=null) stmt.close();
				if(con!=null) con.close();
			} catch (SQLException e) {}
		}
	}
}

 


JDBC 프로그램은 기본적으로 AutoCommit 기능이 활성화 되어있어 SQL 명령(DML)이 전달되면 실행되어 자동으로 커밋 처리된다.
->  프로그램 실행시 예외가 발생된 경우 예외 발생전 전달되어 실행된 SQL 명령에 대한 롤백 처리가 불가능하다.

따라서 JDBC 프로그램에서 AutoCommit 기능을 비활성화 처리한 후

예외 발생 없이 프로그램이 정상적으로 실행되면 커밋 처리하고, 예외가 발생된 경우 롤백 처리하는 것을 권장한다. 

STUDENT 테이블에 저장된 학생정보 중 학번이 [2000]인 학생의 이름을 [임꺽정]으로 변경하는 JDBC 프로그램 작성

package xyz.itwill.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class TransactionControlApp {
	public static void main(String[] args) {
		Connection con=null;
		Statement stmt=null;
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
			
			String url="jdbc:oracle:thin:@localhost:1521:xe";
			String user="scott";
			String password="tiger";
			con=DriverManager.getConnection(url, user, password);

			//Connection.setAutoCommit(boolean autoCommit) : AutoCommit 기능의 사용유무를 변경하는 메소드
			// => false : AutoCommit 기능 비활성화, true : AutoCommit 기능 활성화 - 기본값
			con.setAutoCommit(false);
			
			stmt=con.createStatement();
			
			String sql="update student set name='임꺽정' where no=2000";
			int rows=stmt.executeUpdate(sql);
			
			//if(con!=null) throw new Exception();//인위적 예외 발생
			
			if(rows > 0) {//조작행이 있는 경우
				System.out.println("[메세지]"+rows+"명의 학생정보를 변경 하였습니다.");
			} else {//조작행이 없는 경우
				System.out.println("[메세지]변경 처리할 학번의 학생정보를 찾을 수 없습니다.");
			}
			
			//Connection.commit() : 커밋 처리하는 메소드 - 전달된 SQL 명령이 실제 테이블에 적용
			con.commit();
		} catch (Exception e) {
			System.out.println("[에러]프로그램에 예기치 못한 오류가 발생 되었습니다.");
			try {
				//Connection.rollback() : 롤백 처리하는 메소드 - 전달된 SQL 명령의 실행을 취소 
				con.rollback();
			} catch (SQLException e1) { }
		}  finally {
			try {
				if(stmt!=null) stmt.close();
				if(con!=null) con.close();
			} catch (SQLException e) { }
		}
	}
}