본문 바로가기
클라우드 활용 자바개발자 양성과정/02-2. JDBC

1. JDBC 기본

by moca7 2024. 7. 29.

 

ㅁ 자바와 오라클 연동

- 실제 DB상의 데이터자바 프로그램 상에서 조회하거나, DB에 데이터를 insert 하거나 update, delete 한다.

 

 

ㅁ (sql developer) 관리자 계정에서 계정 만들기


CREATE USER JDBC IDENTIFIED BY JDBC;
GRANT CONNECT, RESOURCE TO JDBC;

 

 

ㅁ (sql developer) 테이블 생성하고 데이터 삽입

 

 

- DML문은 실제 DB에 반영되지는 않음. 트랜잭션에만 쌓여 있다. COMMIT을 해야 실제 DB에 적용된다. 

자바 프로그램에서 DB의 데이터를 조회할 때 실제 DB로부터 조회함. 

 


- CRUD : CREATE(INSERT), READ(SELECT), UPDATE(UPDATE), DELETE(DELETE)

- MVC 패턴에서 Model은 DAO (Data Access Object)와 같은 데이터 접근 객체를 포함할 수 있습니다.

DAO는 데이터베이스와의 CRUD (Create, Read, Update, Delete) 작업을 담당합니다.

 

 

 (sql developer)  접속 창에서 새 접속(JDBC 계정) 만들기

- 세부정보 탭우리가 접속할 DB 서버에 대한 정보.

- 원래는 호스트 이름DB 서버의 IP주소를 쓰는데, localhost는 각자 PC의 IP주소가 제시된거.

- 포트 1521

- SID(I) xe                   //      서비스 아이디

 

 

 

 

 

ㅁ C드라이브 - workspace 폴더에 새로 03_jdbc-worksapce 폴더를 만든다.

- C:\workspaces\01_java-workspace에서 .gitignore 파일이 git에 올리면 안되는 파일들 모아놓은 것.

이 파일을 복사해서 03_jdbc-workspaces에 복붙. (이걸 지금 맨 처음에 해야 함)

- 그리고 이클립스 키고 워크스페이스를 workspace 03_jdbc-worksapce로 지정한다. (gitignore 복붙 먼저)

 

 

ㅁ 이클립스 새로 키고 초기설정

- 설정들은 처음 한번만 하면 저장된다. 03_jdbc-workspace로 워크스페이스 지정하고 이클립스 키면 .metadata 폴더가 생기는데 저 폴더에 내 세팅이 저장된다. 이건 절대 git으로 공유하면 안 된다. (내 설정이니까)

 

(1) 오른쪽 상단 Open Perspective - 현재 java EE(웹개발환경)인데 java 환경으로 변경.

(2) 필요없는 뷰 다 끄기.

상단 Window 메뉴 - show view에서 console 뷰 하나만 키기.

(3) 이클립스 새로 키면 무조건 인코딩 설정. UTF-8로.

상단 Window 메뉴 - Preferences - General - Workspace에서 Text file encoding - UTF-8로 설정하기.

상단 Window 메뉴 - Preferences - General - editors - Text Editors - Spelling에서 Encoding을 UTF-8로 설정하기.

(4) 폰트 변경

General - Appearance - Colors and Fonts에서 Baisc 안의 text fonts에서 D2Coding 설택.

(5) 메시지 자동완성(try~catch 등) 주석 없애는 Template화 작업하기

상단 Window 메뉴 - Java - Code Style - Code Templates 에서 Code - Method body 더블클릭해서 주석 삭제.

상단 Window 메뉴 - Java - Code Style - Code Templates 에서 Code - Catch block body 더블클릭해서 주석 삭제.

 

 

ㅁ (이클립스) 상단 메뉴 File - new - Java Project

- 프로젝트 이름은 01_JDBC_Basic. 하단의 Module 탭 체크 해제하고 Finish.

- 패키지는 적어도 3번째 레벨 이상으로 만들기를 권장함.

- 첫번째, 두번째 패키지는 도메인의 역순으로 많이 함.

세번째 패키지는 프로젝트명이나 팀명으로 많이 함.

 

 

 

ㅁ 우리는 Oralce DBMS를 사용. 그래서 그에 맞는 드라이버 정보를 제시해야 한다.

오라클에서 제공하는 드라이버를 적용해서 연동한다.

 

- C드라이브에서 파일 하나를 찾는다. 근데 깊숙한 곳에 있어서 찾기 쉽게 복사해서 갖다 놓을 것.

C드라이브에 dev라는 폴더 만든다.

개발자마다 자주 쓰는 파일을 따로 폴더로 관리함.

이제부터는 자주 쓰는 파일들을 dev라는 폴더에 가져다 놓는다.

 

- C드라이브 oraclexe 폴더(오라클 설치시 경로 지정 따로 안하면 c드라이브에 설치되면서 만들어짐) - app - oracle - product - 11.2.0(버전) - server - jdbc - lib 폴더에 3개의 jar 파일이 있다.

이 중 ojdbc6에 자바 프로그램과 오라클 DBMS를 연동시키는데 필요한 드라이버 정보가 있다.

C드라이브 dev 폴더에 복붙하기.

 

 

 

ㅁ < JDBC > 

- Java DataBase Connectivity

- 자바 프로그램상에서 DB와 연동(접근)할 수 있게 도와주는 API

- 주요 구성요소 : JDBC 드라이버, Connection, Statement, ResultSet, SqlException

 

- JDBC용 객체가 있다. (순서대로 객체를 만들어야 함. 절차 중요)

(1) DriverManager                     : Connection 객체를 생성하기 위한 객체

(2) Connection                           : DB에 접속해서 DB의 연결정보를 담고 있는 객체

(3) [Prepared] Statement          : 연결된 DB에 sql문을 전달해서 실행하고, 그 결과를 받아내는 객체

(4) ResultSet                              : select문 실행 시 조회된 결과물들이 담겨있는 객체

 

 

ㅁ ojdbc6은 압축 파일인데 클래스들이 많이 있다. 그 중 하나가 드라이버. 

ojdbc6은 라이브러리다.

 

 

JDBC 절차

 

(1) jdbc driver 등록

- 해당 DBMS(오라클)가 제공하는 Driver 클래스 등록

 

(2) Connection 객체 생성

- 연결하고자하는 DB정보를 입력해서 해당 DB와 연결하면서 생성한다.

 

(3) Statement 객체 생성

- Connection 객체를 이용해서 생성한다.

 

(4) sql문 전달하면서 실행 

- Statement 객체를 이용해서 sql문 실행.

 

(5) 결과 받기

- 결과는 2가지로 나뉜다. select문을 실행한 경우insert, update, delete문을 실행한 경우.

- select문을 실행한 경우에는 조회된 데이터들이 담겨 있는 ResultSet 객체가 돌아온다. 

- insert, update, delete을 실행한 경우에는 int형 숫자값 하나(처리된 데이터 행 수)가 돌아온다.

 

(6-1) 트랜잭션 처리(insert, update, delete를 실행한 경우)

- 성공일 경우 실제 DB에 반영되도록 commit, 실패일 경우 rollback한다.

 

(6-2) select문을 실행한 경우

- ResultSet에 담겨있는 데이터들을 하나씩 뽑아서(rset.getInt("컬럼명")) vo객체에 주섬주섬 옮겨 담기.

- 한 행이면 한 vo객체에 담는다. 여러 행이면 list에 담는다.

 

(7) 다 사용한 JDBC용 객체 자원 반납 (close) <- finally

- 안하면 나중에 DB가 뻗어버림.

- 유의할 점은 생성한 역순으로 반납해야 한다.

 

 

 

Class.forName()은 Java에서 클래스 로딩을 동적으로 수행하는 메서드입니다.

 

  1. 동적 클래스 로딩:
    • 런타임에 클래스의 이름을 문자열로 지정하여 클래스를 로드할 수 있습니다. 이 방법은 컴파일 시점에 클래스가 존재하지 않거나, 애플리케이션이 실행되는 동안에 클래스가 동적으로 결정되는 경우에 유용합니다.
  2. JDBC 드라이버 로딩:
    • JDBC에서 데이터베이스 드라이버를 로드할 때 사용됩니다. 드라이버 클래스를 명시적으로 로드하여 데이터베이스와의 연결을 설정하는 데 사용됩니다.
  3. 클래스 메타데이터:
    • 로드된 클래스의 메타데이터를 얻고자 할 때 사용될 수 있습니다. 예를 들어, 클래스의 필드, 메서드, 생성자 정보를 얻을 수 있습니다.

 

 

ㅁ ResultSet에는 커서라는 개념이 있다.

 

ResultSet의 커서

 

커서는 처음에 데이터를 가리키고 있는게 아니라 컬럼들이 있는 곳을 가리키고 있음.

그래서 한 행 옮겨줘야 한다. 현재 참조하고 있는 행 바꿔주기.

 

 

 

전체 순서 (요약)


0. 사용자에게서 데이터를 입력받을 경우 Scanner 변수 선언과 변수에 데이터 담아두기.
1. 최종적으로 조회 결과를 담아낼 자바 객체 세팅
2. JDBC 과정 중에 필요한 객체 미리 세팅
3. 실행할 sql문 (절대 세미콜론이 있어서는 안 됨)
4. JDBC를 사용한 데이터베이스 쿼리 처리 과정.
  (1) jdbc driver 등록
  (2) Connection 객체 생성
  (3) Statement 객체 생성
  (4), (5) select문을 전달하면서 실행 후 결과(ResultSet) 받기

              or insert, update, delete문을 전달하면서 실행 후 결과(int형) 받기
  (6) 트랜잭션 처리 or ResultSet에 담겨있는 데이터값(컬럼값)들을 뽑아서 vo 객체의 각 필드에 옮겨담기
  (7) 자원 반납
  (8) 결과 리턴

5. 조회된 결과 출력(controller가 view 호출)

 

 

 

전체 순서

 

 

0. 사용자에게서 데이터를 입력받을 경우 Scanner 변수 선언과 변수에 데이터 담아두기.

 

Scanner sc = new Scanner(System.in);

System.out.print("조회하고자 하는 번호 입력: ");

int no = sc.nextInt();

 

 

1. 최종적으로 조회 결과를 담아낼 자바 객체 세팅

 

- insert, update, delete면 int형 변수 선언.       int result = 0;

- select(한 행)이면 vo 객체 선언.                     Member m = null;

- select(여러 행)이면 list 선언.                        List<Member> list = new ArrayList<>();

 

- Member m을 바로 생성하지 않는 이유는 마지막에 조회결과가 있는지 없는지 판단위해.

- result는 0, list는 텅 빈 리스트이다.

- 여러 행이면 보통 List를 사용한다.

ResultSet에서 한 행씩 Test 객체에 담고, 또 Test 객체들을 ArrayList에 쌓기.

 

 

2. JDBC 과정 중에 필요한 객체 미리 세팅

- 생성은 나중에하고 선언만 먼저 한다.

 

Connection conn = null;

Statement stmt = null;

ResultSet rset = null;       //     select일 때만

 

- 이것들을 try 블록에서 생성하지 않는 이유는 finally 블록에서 자원 반납시 저 변수들을 사용하기 위해.

 

 

3. 실행할 sql문 (절대 세미콜론이 있어서는 안 됨)

 

String sql = "SELECT TNO, TNAME, TDATE FROM TEST WHERE TNO = 1";

 

String sql = "SELECT TNO, TNAME, TDATE FROM TEST WHERE TNO = " + no;

- sql문은 마지막 변수가 숫자면 마지막에 따옴표 필요 없다.

 

String sql = "INSERT INTO TEST VALUES(SEQ_TNO.NEXTVAL, '" + name + "', SYSDATE)";

- 따옴표 하나랑 따옴표 두 개, 플러스 name 플러스 , 따옴표 두 개랑 따옴표 하나.

 

String sql = "SELECT * FROM MEMBER WHERE USER_ID = '" + userId + "'";

 

System.out.println(sql);

- sql문 잘 작성했는지 확인하면 편하다. PreparedStatement로 미완성된 sql문이면 못 하지만.

 

 

4. JDBC를 사용한 데이터베이스 쿼리 처리 과정.

 

 

(1) jdbc driver 등록

- 자바가 제공하는 것이 아닌 오라클이 제공함.

 

Class.forName("oracle.jdbc.driver.OracleDriver");

 

- ojdbc6 파일 안에 OracleDriver 클래스가 있다.

- 패키지나 클래스에 오타 있으면 예외 발생.

- 해당 프로젝트에 ojdbc6.jar 파일을 연동하지 않은 경우 예외 발생.

연동은 왼쪽 패키지 익스플로러에서 프로젝트 우클릭 - properties - java build path에서

Librarires 탭 - Module path - Add external Jars..에서 ojdbc6 라이브러리 추가.

 

(2) Connection 객체 생성

- DB 연결(url, 계정명, 비밀번호)

 

conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "JDBC", "JDBC");

 

- @ 뒤엔 ip 주소가 온다. 

본인 pc의 ip 주소를 쓰면 되는데 localhost를 써도 된다.

- localhost는 DB서버의 호스트 이름 또는 ip 주소.

1521은 포트 번호.

xe는 데이터베이스 서비스의 SID(System Identifier)를 나타냅니다.

xe는 Oracle Express Edition의 기본 서비스 이름입니다.

- 콜론(:)은 구분자.

- 계정명은 대소문자를 구분하지 않지만, 비밀번호는 대소문자를 구분함.

 

(3) Statement 객체 생성

- sql문 실행을 위한 객체

 

stmt = conn.createStatement();

 

- DB 연결정보가 담겨있는 statement 객체가 내부적으로 생성되어서 반환된다.

 

 

(4), (5) select문을 전달하면서 실행 후 결과(ResultSet) 받기

or insert, update, delete문을 전달하면서 실행 후 결과(int형) 받기

 

rset = stmt.executeQuery(sql);

 

- select문 실행일 경우 executeQuery(sql문) <- ResultSet 반환

- insert, update, delete문 실행일 경우 executeUpdate(sql문) <- int 반환

 

 

(6) 트랜잭션 처리 or ResultSet에 담겨있는 데이터값(컬럼값)들을 뽑아서 vo 객체의 각 필드에 옮겨담기

 

 

i) select - 조회 결과가 한 행인 경우

 

if(rset.next()) {  

    t = new Test();

    t.setTestNo(rset.getInt("TNO"));

    t.setTestName(rset.getString("TNAME"));

    t.setTestDate(rset.getDate("TDATE"));

}

 

if(rset.next()) {  

    t = new Test( rset.getInt("TNO"), rset.getString("TNAME"), rset.getDate(3) ); 

}

 

- rset.next()

행 커서를 옮겨주는 역할. 반환형은 boolean. 

커서를 옮긴 곳에 데이터(조회된 행)가 있으면 true, 없으면 false.

 

- t는 데이터가 있는 경우에만 생성된다. 조회된 데이터가 없다면 null인 상태 그대로이다.

- Member 객체 하나가 한 행을 의미함. 

 

- 현재 rset의 커서가 가리키고 있는 한 행의 컬럼값들을 하나씩 뽑아서 Test 객체의 필드에 담기.

rset으로부터 "어떤 컬럼"의 값을 "어떤 자바타입"으로 뽑을 건지 제시한다.

rset.getInt("컬럼명"), rset.getString("컬럼명"), rset.getDate("컬럼명")

 

- 사실 컬럼명 대신 컬럼 순번을 제시해도 된다. 그래도 컬럼명을 제시하는 편이 좋다.

 

 

ii) select - 조회 결과가 여러 행인 경우

- if 대신 while문을 사용해서 모든 행을 스캔한다.

 

while(rset.next()) {

    Test t = new Test(rset.getInt("TNO"), rset.getString("TNAME"), rset.getDate("TDATE"));

     list.add(m);

}

 

 

iii) insert, update, delete인 경우

- select 제외한 DML문은 트랜잭션 처리(커밋, 롤백)해야 한다.

 

if(result > 0) { conn.commit(); }

else { conn.rollback(); }

 

 

(7)  자원 반납

- try 블록을 실행하다 예외가 발생하더라도 무조건 실행되도록 finally 블록에 작성한다.

- 3개 객체를 생성된 역순으로 반납해야 한다.

 

rset.close();         //        select인 경우에만.

stmt.close();

conn.close();

 

 

(8) 결과 리턴 

- return vo객체, return list, return result;

 

 

 

5. 조회된 결과 출력

- 실제론 controller에서 출력문 쓰지 않고, 출력문이 있는 view의 메소드를 호출한다.

 

i) select - 한 행인 경우

 

if( t == null ) { System.out.println("조회 결과가 없습니다."); }

else { System.out.println(t); }     //    vo 객체 Test의 toString() 메소드 실행.

 

 

ii) select - 여러 행인 경우

 

if( list.isEmpty() ) { System.out.println("조회 결과가 없습니다."); }

else  { 

    for(int i=0; i<list.size(); i++) {

        System.out.println( list.get(i) );

    }

}

 

 

iii) insert, update, delete인 경우

 

if(result > 0) { System.out.println("성공적으로 추가되었습니다."); }

else { System.out.println("추가하는데 실패했습니다."); } 

 

 

 

ㅁ 예외 종류

- SQLSyntaxErrorException 

sql문을 잘못쓴 경우

- SQLException : 부적합한 열 이름

rset.getXXX로 컬럼명을 잘못 입력한 경우

 


ㅁ MVC 패턴을 적용시켜서 각 클래스마다 역할을 나눠서 코드를 작성해본다.