[자바/JDBC] 스프링-DB 1편 1. JDBC의 이해
JDBC 이해
애플리케이션을 개발할 때 중요한 데이터는 데이터베이스에 보관한다.
JDBC 등장 이유
데이터 처리를 위해 애플리케이션과 DB 간의 동작 방식은 다음과 같다.
- 커넥션 연결
- SQL(쿼리) 전달
- 결과 응답
여기서 발생할 수 있는 문제가 2가지 있다
- DB에 따른 코드 변경
- 데이터베이스를 변경할 경우(MySQL → Oracle), 각 db 마다 커넥션 연결, 쿼리 전달, 결과 응답 방법이 달라 관련 코드를 매번 수정해야 한다.
- 연결방식 학습
- 개발자가 데이터베이스에 맞는 커넥션 연결, 쿼리 전달, 결과 응답 방법을 새로 학습 해야한다.
→ 이 문제를 해결하기 위해 JDBC라는 자바 표준이 등장했다.
JDBC 표준 인터페이스
JDBC는 Java DataBase Connectivity의 약자로, 자바에서 DB에 접속할 수 있도록 하는 자바 API다.
JDBC는 DB에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다.
- JDBC가 제공하는 기능
- java.sql.Connection : 연결
- java.sql.Statement : SQL을 담은 내용
- java.sql.ResultSet : SQL 요청 응답
인터페이스만으로는 기능이 동작하지 않기에 인터페이스를 구현한 JDBC 드라이버(DB 벤더(회사)에서 자신의 DB에 맞도록 구현한 라이브러리)를 사용해야 DB 기능을 사용할 수 있다.
- 문제 해결
- DB 독립적
- 이제 애플리케이션 로직은 JDBC 표준 인터페이스에만 의존하기 때문에 DB를 변경하고 싶으면 JDBC 구현 라이브러리만 변경하면 된다. 따라서 코드를 변경하지 않아도 된다.
- 간편하고 표준화된 사용법
- 개발자는 이제 JDBC 표준 인터페이스 사용법만 학습하면 된다. 학습에 필요한 시간이 단축되었다
- DB 독립적
- 주의
- 인터페이스로 어느정도 표준화를 했지만, 페이징 SQL은 각 DB마다 사용법이 달라 이 부분은 맞춤으로 설정해야 한다
JDBC와 최신 데이터 접근 기술
JDBC는 출시된지 오래되었고(1997년), 사용법도 복잡하다. 그래서 SQL Mapper, ORM 기술 같이 JDBC를 편리하게 사용하는 다양한 기술이 존재한다.
- SQL Mapper
- 대표 기술 : 스프링 JdbcTemplate, MyBatis
- 장점 - JDBC를 편리하게 사용하도록 도와줌
- SQL 응답 결과를 객체로 편리하게 변환해줌
- JDBC의 반복 코드를 제거해줌
- SQL만 작성할 줄 알면 금방 사용 가능
- 단점
- 개발자가 SQL을 직접 작성해야 함
- ORM
- 대표 기술 : JPA, 하이버네이트(레퍼런스 많고, 디테일한 기능 많이 제공), 이클립스링크
- 객체를 관계형 데이터베이스 테이블과 매핑해주는 기술
- 장점
- ORM이 SQL을 동적으로 생성하기에 반복적인 SQL 직접 작성하지 않아도 됨
- 개발 생산성 향상
- 단점
- 기술이 복잡해 깊이있게 공부한 후 사용해야 함
중요
SQL Mapper, JPA 기술이 JDBC 사용을 편리하게 해주지만, 결국 기반은 모두 JDBC를 사용한다. 따라서 JDBC를 직접 사용하지 않아도 JDBC가 어떻게 동작하는지 기본 원리를 알아두어야 한다. JDBC는 자바 개발자라면 꼭 알아두어야 하는 필수 기본 기술이다.
DriverManager
JDBC가 제공하는 DriverManager는 다음과 같은 기능을 제공한다.
- 라이브러리에 등록된 DB 드라이버들 관리
- 커넥션 획득
- 애플리케이션 로직에서 커넥션이 필요할 시 DriverManager.getConnection() 호출
- DriverManager가 라이브러리에 등록된 드라이버 목록을 자동으로 인식
- 정보를 넘겨 커넥션이 획득 가능한지 확인
- URL(jdbc:[db 이름]:~), USERNAME, PASSWORD
- 이렇게 찾은 커넥션 구현체가 클라이언트에 반환
JDBC 개발 - 생성
JDBC를 이용해 DB 작업을 하려면 DriverManager로 DB와 연결 후 SQL을 보낸 뒤 결과를 받고 종료하는 순서로 로직을 작성하면 된다.
Member 테이블에 회원을 등록하는 예시인데, 구체적인 코드는 다음과 같다.
public Member save(Member member) throws SQLException {
String sql = insert into member(member_id, money) values(?, ?); // " 인식 문제로 " 뺐음
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate();
return member;
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
private void close(Connection con, Statement stmt, ResultSet rs) {
// 실행한 역순서(ResultSet -> PreparedStatement -> Connection)로 객체 종료
// ResultSest null 체크 후 종료
// Statement null 체크 후 종료
// Connection null 체크 후 종료
}
private Connection getConnection() {
return DBConnectionUtil.getConnection();
}
JDBC 개발 - 조회
이전에 저장한 데이터를 조회해보자. 대부분의 로직이 생성 로직과 비슷하지만, 결과를 반환하는 ResultSet과 Cursor 내용이 다르다.
public Member findById(String memberId) throws SQLException {
String sql = select * from member where member_id = ?; // " 인식 문제로 " 뺐음
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
rs = pstmt.executeQuery();
if (rs.next()) {
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId=" + memberId);
}
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, rs);
}
}
- rs.next()
- ResultSet 내부에 있는 커서를 이동해 다음 데이터를 조회할 수 있다
- 위 함수를 이용하면 커서가 다음으로 이동한다.
- 최초의 커서는 데이터를 가리키고 있지 않기 때문에 rs.next()를 최초 한번은 호출해야 데이터 조회가 가능하다
- rs.next() == true : 커서 이동 결과 데이터가 존재한다
- rs.next() == false : 커서 이동 결과 데이터가 존재하지 않는다(더 조회할 데이터가 없다)
Reference
인프런 김영한 - '스프링 DB 1편 - 데이터 접근 핵심 원리'