JDBC 개요
JDBC 등장 배경
일반적으로 애플리케이션이 데이터베이스와 통신하려면 다음 과정을 거친다
- 커넥션 연결: TCP/IP를 사용하여 DB에 연결
- SQL 전달: 애플리케이션에서 SQL을 DB에 전송
- 결과 응답: DB가 SQL을 실행하고 결과를 반환
그러나, 데이터베이스마다 커넥션 방식, SQL 전달 방식, 응답 처리 방식이 달라 개발자는 매번 새로운 DB에 맞춰 코드를 변경해야 하는 문제가 있었다..
=> 이를 해결하기 위해 JDBC(Java Database Connectivity)가 등장
JDBC 표준 인터페이스

- JDBC는 자바에서 데이터베이스에 접속할 수 있도록 설계된 표준 API
- 애플리케이션과 데이터베이스 사이의 의존성 역전을 적용한 것
- JDBC에서 제공하는 표준 인터페이스
java.sql.Connection
: DB 연결 관리java.sql.Statement
: SQL 실행java.sql.ResultSet
: 쿼리 결과 처리
- 개발자는 이 표준 인터페이스를 사용하여 데이터베이스 작업을 수행하면 됨
- 각각의 데이터베이스마다 커넥션 연결, SQL 전달, 결과 응답 받는 방법을 새로 학습할 필요가 없음!
- 하지만 JDBC 인터페이스 자체로는 동작하지 않으며, 각 DB 벤더에서 제공하는 JDBC 드라이버가 필요
- 이는 각각의 DB 벤더(회사)에서 자신의 DB에 맞도록 구현해서 라이브러리로 제공 ex)
mysql-connector-j
- 이는 각각의 DB 벤더(회사)에서 자신의 DB에 맞도록 구현해서 라이브러리로 제공 ex)
JDBC의 장점
- DB 변경 시 코드 수정 최소화: JDBC 표준 인터페이스를 사용하면 DB 벤더가 바뀌어도 JDBC 드라이버만 변경하면 되므로 애플리케이션 코드 변경이 최소화됩니다.
- 학습 부담 감소: 개발자는 JDBC 사용법만 익히면 다양한 DB에 적용할 수 있습니다.
참고: SQL 자체는 DB마다 일부 차이가 있으며, JPA를 사용하면 이런 차이를 더욱 줄일 수 있다.
JDBC와 최신 데이터 접근 기술
JDBC는 1997년에 등장한 오래된 기술이며, 사용이 번거로움..
=> 최근에는 JDBC를 직접 사용하기보다는 이를 편리하게 다룰 수 있는 기술이 등장함.
SQL Mapper
- 장점: 반복적인 JDBC 코드를 줄여주고 SQL 응답을 객체로 변환해줌
- 단점: SQL을 직접 작성해야 함
- 대표 기술:
JdbcTemplate
,MyBatis
ORM (Object-Relational Mapping)
- 특징: 객체와 DB 테이블을 자동으로 매핑하여 SQL 작성을 최소화
- 장점: SQL을 직접 작성하지 않아도 되며, DB 변경 시 유연한 대응 가능
- 단점: 학습 곡선이 가파름
- 대표 기술: JPA (자바 진영 ORM 표준 인터페이스), Hibernate, EclipseLink (JPA 구현체)
주의: 내부적으로 모든 기술이 JDBC를 사용하므로 JDBC의 기본 개념은 반드시 알아야 함.
JDBC 사용
DB 연결 설정
@Slf4j
public class DBConnectionUtil {
public static Connection getConnection() {
try {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
log.info("get connection={}, class={}", connection, connection.getClass());
return connection;
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}
DriverManager.getConnection(..)
을 사용하여 DB 커넥션을 획득- 라이브러리에 등록된 JDBC 드라이버가 자동으로 감지되어 적절한 커넥션을 반환
- JDBC DriverManager 연결 흐름
DriverManager.getConnection()
호출DriverManager
가 등록된 JDBC 드라이버를 탐색- URL을 분석하여 적절한 드라이버를 선택
- 선택된 드라이버가
Connection
객체를 생성하여 반환
JDBC를 이용한 읽기와 쓰기
데이터 읽기 (Read)
public Member findById(String memberId) throws SQLException {
String sql = "SELECT * FROM member WHERE member_id = ?";
try (Connection con = getConnection();
PreparedStatement pstmt = con.prepareStatement(sql)) {
pstmt.setString(1, memberId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return new Member(rs.getString("member_id"), rs.getInt("money"));
} else {
throw new NoSuchElementException("Member not found: " + memberId);
}
}
}
}
PreparedStatement
를 사용하여 SQL 인젝션 방지ResultSet
을 사용하여 데이터 조회rs.next()
를 통해 데이터가 존재하는지 확인 후 객체 생성
ResultSet
- 보통 select 쿼리의 결과가 순서대로 들어가 저장되는 자료구조
- 내뷍 커서(cursor)를 이동시켜 다음 데이터 조회 가능
rs.next()
: 커서를 다음으로 이동시킴. 최초의 커서는 데이터를 가리키고 있지 않기에rs.next()
를 최초 한번은 호출해야 데이터를 조회할 수 있음rs.next()
의 결과가 true면 데이터가 있는 것이고, false면 더 이상 데이터가 없다는 뜻- `rs.getString("member_id"): 현재 커서가 가리키고 있는 위치의 member_id 데이터를 String 타입으로 반환
데이터 쓰기 (Create, Update, Delete)
public Member save(Member member) throws SQLException {
String sql = "INSERT INTO member(member_id, money) VALUES(?, ?)";
try (Connection con = getConnection();
PreparedStatement pstmt = con.prepareStatement(sql)) {
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate();
return member;
}
}
executeUpdate()
로 데이터 삽입try-with-resources
문법으로 자동 리소스 해제- 수정 및 삭제도 마찬가지임. sql만 바꿔주면 됨
DataSource 이해
커넥션을 얻는 방법은 앞서 학습한 JDBC DriverManager를 직접 사용하거나, 커넥션 풀을 사용하는 등 다양한 방법이 존재한
다. 애플리케이션에서 DriverManager를 사용해서 커넥션을 획득하다가 HikariCP 같은 커넥션 풀을 사용하도록 변경하면 커넥션을 획득하는 애플리케이션 코드도 함께 변경해야 한다..
=> 커넥션을 획득하는 방법을 추상화 하기 위한 추상화 인터페이스(DataSource)를 제공 (= 의존성 역전)
(JDBC 등장 배경과 유사)

DataSource 인터페이스
자바에서는 커넥션 획득 방식을 추상화하기 위해 javax.sql.DataSource 인터페이스를 제공한다.
public interface DataSource {
Connection getConnection() throws SQLException;
}
- 대부분의 커넥션 풀은 DataSource 인터페이스를 이미 구현하고 있으며, 이를 사용하면 커넥션 풀 기술을 쉽게 교체할 수 있다.
- DriverManager는 DataSource 인터페이스를 사용하지 않으므로, 스프링에서는 DriverManagerDataSource 클래스를 제공하여 이를 해결한다.
DataSource - DriverManager 사용 예시
@Test
void dataSourceDriverManager() throws SQLException {
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
useDataSource(dataSource);
}
private void useDataSource(DataSource dataSource) throws SQLException {
Connection con1 = dataSource.getConnection();
Connection con2 = dataSource.getConnection();
log.info("connection={}, class={}", con1, con1.getClass());
log.info("connection={}, class={}", con2, con2.getClass());
}
DataSource - 커넥션 풀 (HikariCP) 사용 예시
@Test
void dataSourceConnectionPool() throws SQLException, InterruptedException {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
dataSource.setMaximumPoolSize(10);
dataSource.setPoolName("MyPool");
useDataSource(dataSource);
Thread.sleep(1000); // 커넥션 풀 초기화 대기
}
- HikariCP를 사용하여 커넥션 풀을 구성하며, 커넥션 재사용을 통해 성능을 향상시킬 수 있다.
DataSource 적용 예제
MemberRepository
@Slf4j
public class MemberRepository {
private final DataSource dataSource;
public MemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
private Connection getConnection() throws SQLException {
Connection con = dataSource.getConnection();
log.info("get connection={}, class={}", con, con.getClass());
return con;
}
}
- DataSource를 의존성 주입하여 DB 연결을 수행
- 직접 DriverManager를 사용하는 것이 아니라 DataSource 인터페이스를 활용하여 커넥션을 획득
정리
- JDBC는 자바의 표준 DB 연결 API이며, 기본 개념을 익혀야 한다.
- DriverManager를 통해 JDBC 드라이버를 로드하고 DB에 연결한다.
- PreparedStatement를 사용하여 SQL 인젝션을 방지한다.
- JDBC는 직접 사용하기 복잡하므로, SQL Mapper(JdbcTemplate, MyBatis)나 ORM(JPA, Hibernate)와 함께 사용하는 것이 일반적이다.
- DataSource를 사용하면 커넥션 풀을 활용하여 성능을 향상시킬 수 있다.
'Java' 카테고리의 다른 글
[Java] 소켓 통신과 네트워크 예외 (0) | 2025.01.10 |
---|---|
[Java] 제네릭(Generics) (0) | 2025.01.07 |
[Java] 스레드 풀 & ExecutorService (0) | 2025.01.07 |
[Java] 동시성 컬렉션 (0) | 2025.01.06 |
[Java] CAS - 동기화와 원자적 연산 (1) | 2025.01.06 |