[JPA] JDBC
- [ Backend ]/Spring DB, JPA
- 2023. 2. 5.
애플리케이션 서비스를 개발할 때 데이터는 DB에 보관하고, DB와의 연결을 위해서 애플리케이션(클라이언트)은 애플리케이션 서버를 이용하여 DB와의 연결을 구축했다.
이 연결은 구축된 커넥션을 통해서 sql을 전달하면 db에서 결과를 응답받는 식으로 진행되었는데, 문제는 DB의 종류마다 이 커넥션의 방법이 달라서 DB를 변경하면 애플리케이션 서버의 DB관련 코드도 변경해주어야 했다는 것이다.
이러한 불편함을 해소하기 위해서 JDBC라는 자바 커넥션 표준이 등장하게 된다. JPA의 구현체인 hibernate도 내부적으로 이러한 JDBC를 사용하여 DB와 통신한다.
JDBC (Java DB Connectivity)
JDBC는 자바에서 데이터베이스에 접근하기 위한 API들을 제공하는 라이브러리로, 연결-SQL 전달-결과 응답이라는 3개의 과정을 인터페이스로 정의한다. 이렇게 정의된 인터페이스는 JDBC마다 각각의 DB에 맞게 설계된 드라이버를 이용하여 각각의 DB에 접근할 수 있게 된다.
이렇게 하면 개발자는 JDBC 표준 인터페이스에만 맞추어 개발하면, 드라이버가 각각의 DB에 맞게 변환해주기 때문에 불필요한 코드 수정을 방지할 수 있게 된다.
(앞서 말한 연결, sql전달, 결과 응답의 3단계는 각각
연결: java.sql.Connection
전달: java.sql.PreparedStatement (java.sql.Statement를 상속)
응답: java.sql.ResultSet
이라는 3개의 인터페이스를 이용한다.)
public Member findById(int memberId) throws SQLException {
String sql = "select * from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
private final String URL = ~; //db url
private final String USERNAME = ~; //db username
private final String PASSWORD = ~; //db password
try {
try {
con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (SQLException e) {
log.error("db connection error", e);
throw new IllegalStateException(e);
}
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, memberId);
rs = pstmt.executeQuery();
if(rs.next()) {
Member member = new Member();
member.setMemberId(rs.getInt("member_id"));;
member.setName(rs.getString("name"));
return member;
}
else {
throw new NoSuchElementException("member not found memberId=" + memberId);
}
}
catch (SQLException e) {
log.error("db error", e);
throw e;
}
//가져온 외부 리소스 사용 종료
finally {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
log.error("error", e);
}
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
log.error("error", e);
}
}
if(con!=null) {
try {
con.close();
} catch (SQLException e) {
log.error("error", e);
}
}
}
}
-> jdbc를 이용한 select문 예시이다. 쿼리문을 직접 작성해주고 ?에는 파라미터 바인딩을 통해서 SQL인젝션 공격을 예방할 수 있다. 하지만 connection연결 - PreparedStatement를 이용한 파라미터 바인딩 - ResultSet을 사용하여 반복문으로 결과 가져오기 라는 반복적인 작업을 필요로 하는데다가 복잡한 예외처리 과정까지 신경써야 하기 때문에 매우 번거롭다.
※ 위 과정처럼 커넥션을 새로 생성하기 위해서는 DB와 tcp/ip 네트워크 통신 과정을 사용해야 하는데, 그 과정에서 리소스를 잡아먹게 된다. 대용량 서비스라면 응답속도에 큰 영향을 줄 수도 있기 때문에, HikariCP와 같은 커넥션 풀을 사용하여 DB와 연결이 완료된 커넥션들을 사용하는 방법이 있다. (Spring JPA는 이 방법을 사용한다)
※ DB별로 공통된 부분은 JDBC가 해결해주지만, 이 바깥의 부분은 해결 불가능한데다 개발자가 sql문을 일일히 작성해주어야 하기 때문에 최근에는 JPA와 같은 ORM(객체를 RDB와 매핑하는 기술)을 이용한다. 이 기술들도 모두 JDBC를 기본으로 한다.
'[ Backend ] > Spring DB, JPA' 카테고리의 다른 글
[JPA] 쓰기 지연으로 인한 서비스 오류 경험 (0) | 2023.06.21 |
---|---|
[JPA] 연관관계 설정 고민 (0) | 2023.05.07 |
[QueryDsl] QueryDsl 사용해보기 (0) | 2022.11.22 |
[JPA] 순환참조, 무한참조 (0) | 2022.07.26 |
[Spring JPA] 변경감지 vs 병합 (0) | 2022.02.02 |