JDBC는 Java DataBase Connectivity의 약어로 자바 프로그램에서 다른 기종 간의 데이터베이스를 표분화된 방법으로 접속할 수 있도록 만든 API 규격이다. 개발자는 데이터베이스 종류와 관계없이 표준화된 API를 이용해 프로그램을 만들 수 있다.
JDBC의 개념
데이터베이스는 종류가 매우 다양하다. 이로 인해 응용 프로그램에서 데이터베이스에 접속하고 데이터를 처리하는 방법이 제각각이기 때문에 개발에 많은 어려움이 있다. 이러한 문제를 해결해주는 JDBC는 자바 애플리케이션에서 표준화된 방법으로 다양한 데이터베이스에 접속할 수 있도록 설계된 인터페이스다. 따라서 애플리케이션 개발자는 각 데이터베이스에 대해 자세히 알지 못해도 JDBC API만 알면 모든 데이터베이스에서 동작할 수 있는 애플리케이션을 개발할 수 있다.
응용 프로그램에서는 자바에 기본적으로 포함된 JDBC API(인터페이스로 규격만 정의하고 있음)를 사용해 프로그램 코드를 작성하고 실제 데이터베이스 연결은 각 데이터베이스 회사가 제공하는 JDBC 드라이버(JDBC API 구현 클래스)를 이용해 SQL 문으로 데이터를 조작하는 형태로 동작한다.
JDBC 드라이버 설치
JDBC는 자바 인터페이스로 정의된 일종의 규격이기 때문에 실제 구현된 클래스가 없으면 동작하지 않는다. 이때 실제 구현된 클래스를 JDBC 드라이버라고 부르며 라이브러리와 같은 개념이다. JDBC API를 사용하는 프로그램을 개발하거나 실행하는 과정에 해당 라이브러리가 반드시 필요하다.
Maven 기반 프로젝트일 경우 다른 라이브러리와 마찬가지로 pom.xml에 의존선을 추가하면 자동으로 프로젝트에서 참조된다. 또한 톰캣을 통해 실행하는 과정에도 함께 패키징되기 때문에 별도의 과정 없이 사용할 수 있다.
Maven을 사용하지 않을 경우 [WEB-INF/lib] 폴더에 관련 라이브러리 파일을 복사해두어야 한다,
JDBC 드라이버는 보통 데이터베이스를 만드는 회사에서 직접 배포하는데, 예를 들어 오라클의 경우 오라클 DB에 포함되어 있거나 별도 다운로드를 제공한다. H2는 데이터베이스 패키지 자체에 드라이버 클래스가 포함된 구조이지만 다른 데이터베이스의 경우 데이터베이스 엔진과 JDBC 드라이버는 완전히 분리된 구조다.
이클립스에 H2 데이터베이스 드라이버를 추가하기 위해 'pom.xml'의 <dependencies>...</dependencies> 사이에 다음과 같이 H2 데이터베이스 의존성을 추가한다. 메이븐 리포지터리(mvnrepository.com)에서 'h2'를 검색해 버전을 선택해도 된다. 이때 <scope>...</scope> 부분은 반드시 제거하고 'pom.xml'에 복사해야 한다.
<dependencies>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
</dependencies>
파일을 저장하면 자동으로 h2 라이브러리를 다운로드하는 과정이 백그라운드로 진행된다(완료될 때 까지 조금 기다려야 한다). [Java Resources]→[Libraries]→[Maven Dependencies]에 'h2-1.4.200.jar'이 추가되어 있으면 정상적으로 등록된 것이다. 만일 시간이 지나도 추가 되지 않을 경우 프로젝트를 선택한 다음 마우스 오른쪽 버튼을 클릭하고 [Maven]→[Update Project]를 선택한 후 <OK>를 클릭한다.
JDBC 프로그래밍 과정
JDBC 프로그래밍은 자바에서 데이터 베이스 연동 프로그램을 개발하는 것을 말한다. 웹에서뿐만 아니라 일반 자바 애플리케이션에서도 동일하게 사용할 수 있으니 참고한다.
JDBC API는 다양한 기능을 제공하기 때문에 상세한 기능은 API 문서를 참고하는 것이 좋다. JDBC 프로그래밍의 기본 단계와 사용 클래스는 다음과 같다.
1단계: JDBC 드라이버 로드
데이터베이스에 접속하려면 먼저 해당 데이터베이스의 JDBC 드라이버를 로드해야 한다. 이러한 과정이 필요한 이유는 JDK에 포함된 JDBC는 API 규격을 위한 자바 인터페이스로 실제로 동작하려면 각각의 데이터베이스 공급 업체에서 구현한 드라이버 클래스가 사전에 로딩되어야 하기 때문이다.
JDBC 드라이버를 로드하는 방법은 크게 두 가지가 있다. jdbc.drivers라는 시스템 환경 변수에 등록된 내용으로 하는 방법과 Class.forName() 메서드를 이용해서 직접 해당 클래스를 로드하는 방법이다. 대부분의 경우 다음 코드와 같이 Class.forName() 메서드를 사용한다.
Class.forName("org.h2.Driver");
드라이버 로드에 사용되는 org.h2.Driver는 H2 데이터베이스의 드라이버 클래스로 이름을 잘못 명시하면 오류가 발생한다. 데이터베이스마다 구현한 클래스 이름이 다르므로 다른 데이터베이스를 사용할 경우 해당 클래스 이름을 사전에 알아두어야 한다.
대표적인 JDBC 드라이버 클래스 이름은 다음과 같다.
- 오라클: oracle.jdbc.driver.OracleDriver
- MySQL: com.mysql.jdbc.Driver, com.mysql.cj.jdbc.Driver(8.x)
2단계: 데이터베이스 연결
드라이버가 로드되면 해당 데이터베이스의 JDBC 드라이버를 이용해 프로그램을 작성할 수 있는 상태가 된 것을 의미한다. 실제 데이터베이스와 연결하려면 Connection 클래스의 인스턴스가 필요하며, DriverManager.getConnection() 메서드를 이용해서 레퍼런스를 가져올 수 있다. 또한 JDBC URL, DB 사용자 아이디/비밀번호가 필요하다.
JDBC URL
JDBC URL은 데이터베이스에 대한 다양한 정보를 포함한다. 각 데이터베이스별로 JDBC URL이 다르므로 사용하는 데이터베이스 매뉴얼을 참고해서 작성해야 한다.
JDBC URL 구조는 다음과 같다.
- jdbc:하위 프로토콜:데이터 원본 식별자
H2의 경우 DB 실행 방식에 따라 다음과 같은 형식을 취한다.
// 임베디드 모드(파일 직접 접속)
jdbc:h2:~/test // 윈도우 사용자 홈디렉터리에 'test.mv.db' 파일 사용
jdbc:h2:e:/Dev/test // e드라이브의 [Dev] 폴더에 'test.mv.db' 파일 사용
// 네크워트 서버 모드
jdbc:h2:tcp:/ /localhost/~/test // 네트워크로 접속 허용, 데이터 파일은 사용자 홈디텍터리에 위치
// 메모리 DB 모드(종료 시 데이터 소멸됨)
jdbc:h2:mem:test_mem
데이터베이스에 연결하려면 이와 같은 모든 정보를 정확하게 알고 있어야 하며, 이 중에서 한 가지라도 잘못되어 있으면 제대로 연결되지 않는다.
대표적인 JDBC URL은 다음과 같다.
- 오라클: jdbc:oracle:thin:@localhost:1521:SID
- MySQL: jdbc:mysql://localhost/myDB??serverTimezone=UTC
Connection 클래스 인스턴스 레퍼런스 얻기
JDBC 클래스 로딩과 URL이 준비되었으면 실제 데이터베이스와의 연결을 만들기 위한 코드를 작성한다. DriverManager의 getConnection() 메서드를 사용한다.
Connection conn = DriverManager.getConnection(JDBC_URL,"아이디","비밀번호");
- JDBC_URL: 해당 데이터베이스에 맞게 미리 정의되어 있는 문자열이다.
- 아이디와 비밀번호: 데이터베이스에 등록된 계정이다.
3단계 : Statement 생성
데이터베이스와 연결을 한번 완료하면 이후 연동부터는 SQL 문을 통해 이루어진다. 이때 문자열로 이루어진 SQL 문을 JDBC에서 처리할 수 있는 객체로 변환해야 하는데, 이때 사용되는 것이 Statement 객체다. 하지만 보통 SQL 문과 데이터를 조합하기 때문에 일반 Statement보다는 Statement를 상속받는 PreparedStatement를 사용하는 것이 좋다.
PreparedStatement는 SQL 문을 미리 만들어두고 변수를 따로 입력하는 방식으로 효율성이나 유지보수 측면에서 유리한 구조다. 기본적으로 Statement 클래스를 상속받기 때문에 Statement 클래스 메서드를 모두 사용할 수 있다.
PreparedStatement를 사용할 때는 SQL 문 중간중간 데이터가 들어가야 하는 부분에 '?'를 넣고, pstmt.setXXX() 형태의 메서드를 통해 데이터를 연결한다. 자료형에 따라 setString, setDate 등 맞는 메서드를 사용해야 한다. 데이터의 순서는 1부터 시작하는 카운트값을 사용한다.
PreparedStatement pstmt = conn.prepareStatement("insert into test values(?,?)");
pstmt.setString(1,request.getParameter("username");
pstmt.setString(1,request.getParameter("email");
4단계: SQL 문 전송
3단계 과정을 통해 PreparedStatement 객체가 준비되었고 실제 쿼리의 실행은 SQL 문 종류에 따라 executeQuery() 혹은 executeUpdate()를 사용하게 된다.
executeQuery()
SELECT 문을 수행할 때 사용한다. 반환값은 ResultSet 클래스 타입으로, 해당 SELECT 문의 결과에 해당하는 데이터에 접근할 수 있는 방법을 제공한다.
executeUpdate()
UPDATE, DELETE와 같은 문을 수행할 때 사용한다. 반환값은 INT 값으로, 처리된 데이터의 수를 반환한다. 이때 executeUpdate() 메서드는 처리한 로우의 개수를 반환하는데 처리된 항목이 없다면 0을 반환한다. 처리 결과를 확인하려면 반환값을 받아서 확인해야 하고, 그럴 필요가 없다면 굳이 반환을 받지 않아도 된다.
5단계: 결과 받기
데이터베이스에서 데이터 결과를 받으려면 Statement나 PreparedStatement의 executeQuery()를 사용한다. 입력, 수정, 삭제와 달리 데이터를 갖고 오는 경우에는 가져온 결과 데이터를 처리하기 위한 ResultSet 객체가 필요하다.
ResultSet rs = pstmt.executeQuery();
ResultSet은 조회한 결괏값에 순차적으로 접근할 수 있는 커서를 다룰 수 있게 한다. 예를 들어 10만 건의 데이터 중 검색 결과가 100건이 나오면, ResultSet은 100건을 로우 단위로 가져올 수 있게 한다.
6단계: 연결 해제
데이터베이스 사용이 종료되면 기본적으로 연결을 해제해야 한다. 예를 들어 고객센터의 전화 통화 시스템을 생각해보면 고객센터에 전화를 걸 때 상담원이 다른 고객과 이미 통화 중이라면 연결이 되지 않는다. 즉 고객센터와 같이 동시에 많은 사람의 전화를 받는 시스템의 경우, 동시 통화가 가능한 수만큼의 상담사가 필요하고 이는 비용 증가로 이어진다.
데이터베이스의 경우에도 동시에 여러 연결을 지원하지만 동시 연결 수에 따라 라이선스 비용이 증가하기도 하고, 동시 연결 가능 수가 적은 경우 대기 시간이 길어지는 문제가 발생하기도 한다. 따라서 사용이 끝난 데이터베이스와의 연결은 해제해주는 것이 좋다. 더불에 데이터베이스와의 연결뿐만 아니라 하나의 연결에서 발생하는 여러 Statement, ResultSet 같은 객체도 바로바로 종료해주는 것이 좋다.
rs.close();
pstmt.close();
conn.close();
다만 응용 프로그램과 데이터베이스의 연결이 요청에 따라 이루어지고 종료되는 구조는 성능상 적합하지 않기 때문에 실제 시스템 구현 시에는 DBCP를 사용하게 된다.
※DBCP※
DBCP(DataBase Connection Pool)는 중간에 미들웨어 혹은 컨테이너에서 데이터베이스와의 연결을 미리 만들어두고 요청 클라이언트에 전달하는 구조를 말한다. 또한 일정 수준의 연결을 유지하면서 요청이 증가하는 경우 미리 연결을 만들거나 하는 자동 관리 기능이 포함되어 있다. 보통 데이터베이스 회사에서 제공하는 구현체를 사용하거나 Apach Commons DBCP, Hikari CP, Tomcat JDBC CP 등의 오픈소스 구현체를 사용하면 된다.
'Programming > Web' 카테고리의 다른 글
[Web] 필터(Filter) (0) | 2022.10.26 |
---|---|
[Web] 리스너(Listener) (0) | 2022.10.26 |
[Web] SQL(DDL, DML) (0) | 2022.10.25 |
[Web] H2 데이터베이스 (0) | 2022.10.24 |
[Web] 관계형 데이터베이스(RDBMS) (0) | 2022.10.24 |