JDBC(Java Database Connectivity)는 애플리케이션과 데이터베이스 연결 동작을 표준화한 자바 API 인터페이스이다. 개발자들은 수많은 데이터베이스의 사용법을 따로 공부할 필요 없이 JDBC 기술 하나만 익히면 자바 애플리케이션에 어떤 데이터베이스든 연결하여 사용할 수 있게 되었다.
요즘에는 순수 JDBC를 사용하는 일은 드물고 MyBatis 등의 SQL Mapper를 통해 편리하게 사용한다. 심지어 최근에 점유율이 높아지고 있는 JPA 등의 ORM 기술을 사용하면 개발자는 쿼리조차 작성할 필요가 없다.
그러나 SQL Mapper든 ORM이든 그 기반에는 JDBC를 통해 동작하고 있기 때문에 JDBC에 대한 이해는 필수이다.
대부분의 애플리케이션은 중요한 정보를 데이터베이스에 저장한다. 일반적으로 서버는 세 단계를 거쳐 DB에 데이터를 저장한다.
초기에는 DB마다 이 세가지 단계를 수행하는 방법이 달랐기 때문에 개발에 어려움이 많았다. 자바는 이 사용법을 표준화하였고, 이것이 JDBC(Java DataBase Connectivity)이다.
JDBC는 대표적으로 다음 세 가지 기능을 인터페이스로 제공한다 :
java.sql.Connection
- 연결java.sql.Statement
- SQL을 담은 내용java.sql.ResultSet
- SQL 요청 응답인터페이스는 틀만 정해져 있을 뿐 세부 동작까지 자세히 정해져있지는 않다. 따라서 각 DB 벤더(제조사)마다 자신의 DB에 맞게 JDBC 인터페이스를 구현한 JDBC 드라이버를 제공한다. 개발자는 DB마다 다른 사용법을 익힐 필요없이 적절한 드라이버만 설치하면 된다.
JDBC의 등장으로 데이터베이스 연결이 편리해졌지만 여전히 한계는 존재한다. DB마다 SQL 문법이 다르기 때문이다. 개발 도중 다른 DB로 변경하고 싶다면 애플리케이션 코드는 변경하지 않아도 되지만 쿼리는 변경해야 한다.
JDBC는 1997년에 출시된 오래된 기술이다. 사용법이 복잡하기 때문에 최근에는 JDBC를 직접 사용하기 보다는 SQL Mapper, ORM 등을 통해 보다 편리하게 사용하고 있다. 하지만 어떤 기술을 사용하든 그 기반에는 JDBC로 동작한다는 것을 기억하자.
일반적으로 JDBC를 통한 SQL 쿼리 실행은 다음 다섯 단계로 이루어진다 :
Conncection
생성 (DB 연결 확보)Statement
생성 (쿼리문 준비)Statement
실행 (쿼리 실행)ResultSet
처리 (결과 처리)Conncection
종료 (DB 연결 종료)Connection
객체를 생성하여 데이터베이스와의 연결을 확보한다.
JDBC가 제공하는 DriverManager
는 DB 드라이버를 관리하고 커넥션을 획득하는 기능을 제공한다. 라이브러리에 등록된 드라이버 목록을 자동으로 인식해서 자신이 전달받은 DB URL 정보에 해당하는 DB를 찾아 연결해준다.
DriverManager.getConnection(url, user, password)
DriverManager
를 직접 사용하는 것보다 DataSource
클래스를 사용하여 커넥션 확보를 추상화 하는 것이 더 좋다.SQL statement에 대응하는 객체인 statement
객체를 생성한다. connection
객체로부터 생성할 수 있다. 문자열로 된 쿼리(sql
)를 인자로 전달한다.
stmt = con.createStatement(sql)
쿼리를 실행하기 위해서는 Statement
의 실행 메서드를 목적에 맞게 호출하면 된다 :
execute()
:boolean
executeQuery()
:ResultSet
SELECT
) 쿼리에 사용. ResultSet
객체는 여러 row 값을 저장할 수 있다.executeUpdate()
:int
INSERT
, UPDATE
, DELETE
의 경우 쿼리 실행으로 영향을 받은 row의 수를 반환한다. CREATE
, DROP
에서는 -1
을 반환한다.ResultSet
에 값을 가져왔다면 next()
메서드를 이용해 결과 값을 차례대로 가져올 수 있다.
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
String name = rs.getString("NAME");
...
}
ResultSet
객체는 여러 개의 결과 row 값을 저장할 수 있으며, 내부에 커서가 존재한다. next()
메서드를 이용해 커서를 한 단계씩 이동할 수 있다. 커서는 최초에 가장 앞부분을 가리키고 있으므로 처음 next()
를 실행하면 첫 번째 값을 가리키게 된다.
next()
메서드는 값이 있는 경우 true
를 반환한다. 값을 가져오고 싶다면 데이터 타입에 맞게 getXXX()
메서드를 사용하면 된다.
while
을, 담긴 값이 분명하게 한 개라면 if (rs.next())
를 사용할 수 있다. 쿼리를 실행하고 나면 커넥션을 종료해야 한다. 리소스 정리는 예외가 발생하든 하지 않든 상관 없이 반드시 해주어야 한다. 만료된 커넥션이 닫히지 않고 남아있는 것을 리소스 누수라고 하며, 커넥션 부족 장애로 이어질 수 있다.
리소스를 정리할 때는 얻을 때와 반대 순서로 정리해야 한다. Statemente
부터 닫고 마지막으로 Connection
을 닫는다(close()
메서드 사용).