JDBC (Java Database Connectivity)
- Java application에서 DBMS를 연동하기 위한 표준 API
- JDBC API를 이용함으로써 DBMS의 종류에 상관없이 동일한 방법으로 데이터베이스 접속 및 질의 실행 가능. 한 번 작성한 코드를 나중에 수정하는 게 어렵기 때문에 코드 수정 없이 Driver만 바꿔주면 됨.
- DBMS 접속 및 이용을 위한 interface(대부분)와 class들을 포함
- DBMS vendor에서 제공하는 JDBC Driver를 통해 구현됨
Java Application <-> JDBC API <-> Oracle용/MS SQL Server용/MySQL용 JDBC Driver <-> Oracle/MS SQL/MySQL DB
JDBC Driver
종류 중 Type 4: Native-Protocol Driver(Thin Driver)
- 100% Java로 구현된 JDBC Driver
- DBMS에 종속적: 각 vendor에서 JDBC API를 구현한 JDBC Driver를 제공
Oracle JDBC Driver : Oracle homepage 또는 Maven repository에서 다운로드 가능
JDBC API
다음 2가지 패키지로 구성됨
1. java.sql package - 이거 사용
- Data source(일반적으로 relational DB)에 저장된 데이터를 접근하고 처리하기 위한 API 제공
- Class: DriverManager
- Interface: Connection, Statement, PreparedStatement, ResultSet 등
2. javax. sql package
- 서버에 존재하는 data source를 접근하고 이용하기 위한 API 제공
- Interface: Data source, XADataSource 등
JDBC API 사용 절차
준비
1. JDBC 관련 Package import
2. JDBC Driver 로딩 및 등록
3. DBMS와의 Connection 획득
사용
4. SQL문 수행을 위한 Statement 객체 생성
5. Statement 객체를 사용해 SQL문 수행(DBMS에 질의 전송 및 실행)
6. DBMS 응답 사용(ResuleSet/return 값 이용)
종료
7. 자원 반납(ResultSet, Statement, Connection)
JDBC Driver 준비
- JDK에 설치 : <JDK 설치폴더>/jre/lib/ext 에 복사
- Tomcat 서버에 설치 : <Tomcat 설치폴더>/lib에 복사
- Web application에 포함 : Java EE Web application의 경우 WEB-INF/lib 폴더에 포함
- Java application은 JDBC Driver 파일을 classpath(경로 저장하는 설정파일)에 포함시켜야 사용 가능
- Eclipse 설정 : project의 Properties -> Java Build Path의 Libraries tab -> Add JARs 또는 Add External JARs -> JDBC Driver 파일 선택
- Maven 설정 (pom.xml)
- dependency 설정으로 간단히 가능
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
1. JDBC Package Import
Java program 내에서 java.sql package import함
ex) import java.sql.*;
2. JDBC Driver Loading 및 등록
Class.forName(CLASS_NAME)
- CLASS_NAME의 이름을 갖는 클래스를 메모리에 로딩
- CLASS_NAME은 프로그램에서 변경 가능하므로 동적으로 특정 클래스 타입의 객체를 생성할 수 있음
Class.forName("oracle.jdbc.driver.OracleDriver");
Oracle Driver를 "" 속 경로 보고 찾는다. 이렇게 호출하면, 해당 클래스의 코드를 byte로 읽어서 메모리에 적재한다. Java에서는 다 메모리에 적재되어야 객체를 생성할 수 있다. 임의의 클래스 경로를 찾아서 메모리에 적재하는 경우에 new로 생성하면, 하드 코딩이다. 단지 문자열이라 바뀔 수가 있는 것이고 그걸 대비하기 위해 객체를 new로 생성하는 것이 아니라 "" 속 경로로 적는다.
3. DBMS와의 연결 획득
1. DriverManager.getConnection() 이용
Connection conn = DriverManager.getConnection(url, user, passwd);
- url
- JDBC를 사용하여 접속할 DBMS Server와 DB의 주소 표현
- 형식: jdbc:[DBMS Server주소:port번호][데이터베이스식별자]
- 예) jdbc:oracle:thin:@dblab.dongduk.ac.kr:1521:orcl(Oracle)
- user, passwd
- DBMS에 접속할 때 사용할 사용자 계정의 이름과 비밀번호
DriverManager Class 속 getConnecton()은 정적 메소드라서 객체 생성 따로 안 하고 바로 사용 가능하며 코드 구현도 다 되어 있다. 이렇게 반환되는 Connection 객체는 연결통로라고 생각하면 된다.
2. javax.sql.DataSource Interface 이용
- 하나의 물리적인 data source를 나타내며, 그것에 대한 연결(connection)을 생성하는 기능(factory 기능) 수행
- DriverManager 방식의 대안으로, connection을 얻기 위한 더 좋은 방법
- 일반적으로 Application Server(ex: Tomcat)에서 클래스 구현 및 객체 생성
- DB connection pooling 기능 지원 (conn 여러 개 생성 가능, 후에 재사용 가능 -> 효율 높임)
- Java Naming and Directory Interface(JNDI) 서비스를 통해 DataSource 객체를 제공
- Application에서는 JNDI 서비스를 검색(lookup)하여 DataSource 객체를 획득 및 사용
javax.naming.Context ctx = new javax.naming.InitialContext();
javax.sql.DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/testdb");
java.sql.Connection conn = ds.getConnection();
4. Statement 객체 생성
Connection #createStatement()
- 정적인 SQL문을 실행하고 그 결과를 반환하기 위해 사용되는 객체
- 실제 createStatement() 구현 코드는 JDBC Driver에 있음
- Connection 인터페이스를 구현한 클래스의 객체가 conn
Statement stmt = conn.createStatement();
5. Statement 객체를 이용해 SQL문 실행
1. 질의(SELECT문) 실행
- sql: 실행할 SQL 질의문
- ResultSet 타입의 객체를 반환
ResultSet rs = stmt.executeQuery(sql);
2. DML(INSERT, UPDATE, DELETE문) 실행
- sql: INSERT, UPDATE, DELETE문
- 삽입,변경,삭제된 행의 개수를 반환
int recordCount = stmt.executeUpdate(sql);
3. 참고
boolean execute(String sql)
- 질의, DML, DDL 모두 실행 가능
- 질의 실행 후 결과가 있으면 true 반환 -> getResultSet()으로 ResultSet 구함
- DML 실행 후 false 반환 -> getUpdateCount()로 행의 개수 구함
6. DBMS 응답 사용
ResultSet
- Statement #executeQuery()의 실행 결과로 반환되는 행들의 집합을 저장
- 내부적으로 커서를 사용해 결과 행들을 순차적으로 접근
ResultSet #next() method
- ResultSet에 저장된 행들을 커서가 순서대로 가리키도록 함.
- 커서는 이미 ResultSet에 정의되어 있고, 내가 구현할 필요 없다.
- 최초 호출 시 첫번째 행을 가리키고 이후 호출될 때 마다 커서를 다음 행으로 이동.
- 더 이상 가리킬 행이 없으면 false를 반환
- 커서를 쓰는 이유는, 만약 수십만개의 결과가 반환될 경우 Java Application 안으로 다 갖고 올 수 없다. 그래서 ResultSet에 저장된 행들을 하나씩 읽는 것이다. 버퍼 같은 느낌.
- ResultSet에서 현재 커서 위치의 컬럼 값 읽기
- ResultSet 객체를 rs가 참조한다고 가정
- rs.getString(컬럼명)
- 컬럼명에 해당하는 필드를 문자열로 읽어 반환
- rs.getString(n)과 같이 숫자를 쓸 경우 질의의 SELECT절에 지정된 n번째 컬럼 값을 반환
7. 자원 반납
데이터베이스 작업이 끝나면 각 자원을 반드시 반환해야 함.
rs.close();
JDBC에서의 SQL문 사용
Statement
- 일반적인 SQL문 사용 시
- SQL문 수행할 때마다 다음 과정 반복
- SQL문 complile -> compile된 SQL문 실행 -> 실행결과 반환
- 반복적인 작업 수행 시 DBMS의 부하 증가
- 동일한 형태의 SQL문이 반복적으로 컴파일됨
- SQL문을 실행할 때마다 DBMS는 SQL문을 컴파일하고 SQL문의 실행을 준비함
- 코드 작성 및 가독성 문제
- 문자열 데이터의 경우 SQL문 내에서 반드시 따옴표(')를 붙여야 함
PreparedStatement
- 사전에 컴파일한 SQL문 실행
- 반복적인 컴파일을 피함으로써 DBMS 부하 감소 효과
- SQL문을 미리 컴파일한 후 실제 데이터 값을 질의 실행 시 지정함
- SQL문 내에 매개변수 사용 가능
- ex) "10"번 부서에 속한 ~ 구해라.
- 매개변수를 제외하고 SQL 문장의 구조가 동일한 경우 사용
PreparedStatement의 사용
1. PreparedStatement 생성
- Connection #preparedStatement() method 이용
- 앞서 살펴본 createStatement() method는 매개변수가 없었는데, preparedStatement() method는 매개변수가 있다. 미리 query를 지정한다는 것이다.
PreparedStatement pstmt = conn.preparedStatement(query);
2. 매개변수 값 지정 및 실행
- 질의문의 ? 자리에 들어갈 값을 지정 후 실행
- prerparedStatement() 는 executeQuery() method에 매개변수를 넣지 않는다.
- 근데 createStatement() 를 상속받기 때문에 매개변수에 query를 넣어도 컴파일 에러가 나지 않는다. 하지만 여기선 덜 완성된 query이기에 넣으면 문제 발생하므로 주의
pstmt.setString(1, "hj");
pstmt.setInt(2, 33);
ResultSet rs = pstmt.executeQuery();
- set methods
- 질의문 상에서 값에 해당하는 부분만 대치 가능
- 테이블 이름은 매개변수로 대치 불가
- 매개변수의 개수와 값을 설정하는 set method 호출의 개수가 일치해야 함
- 값을 설정할 때 값의 타입이 일치해야 함
Metadata 사용
1. DatabaseMetaData interface: 데이터베이스 전반에 관한 메타정보(부가정보) 제공
- Connection #getMetaData()로 획득 (현재 접속한 conn - 특정DB/특정계정)
- Methods
- getDatabaseProductName(), getUserName() 등 계정에 대한 정보
2. ResultSetMetaData interface: ResultSet 객체에 포함된 컬럼들에 대한 메타정보 제공
- 질의 실행 후 ResultSet #getMetaData()로 획득
- Methods
- getColumnCount() 등
- Column Index: 1~n