해당 게시물은 인프런 "스프링 DB 1편 - 데이터 접근 핵심 원리" 강의를 참고하여 작성한 글 입니다.
과거의 수많은 DB들은 모두 사용법이 달랐고 애플리케이션 서버는 DB의 구현법에 의존할 수밖에 없었다.
-> 이를 해결하기 위해 JDBC 자바 표준 인터페이스가 등장한다.
- JDBC 인터페이스를 각 DataBase 회사들이 구현체인 라이브러를 만들어 제공한다.
- 이 라이브러리를 JDBC 드라이버라고 한다.
- 서버 로직의 변경 없이 어떤 DB로든( JDBC 드라이버를 제공하는) 교체할 수 있다.
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
java.sql.Connection은 인터페이스로, 각 DB의 구현체를 DriverManager.getConnection(URL, USERNAME, PASSWORD)로 가져올 수 있다.
이렇게 하면 라이브러리에 있는 데이터베이스 드라이버를 찾아서 해당 드라이버가 제공하는 커넥션을 반환해준다.
- DriverManager는 JDBC가 제공하는 기능으로, 라이브러리에 등록된 드라이버 목록에서 커넥션을 요청한다.
- 여러 DB 드라이버가 있다면 순서대로 URL, USERNAME, PASSWORD를 통해 접근해보고 접근이 되면, 구현체가 반환된다.
String sql = "insert into member(member_id, money) value (?,?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId()); // 첫번째 ?를 member.getMemberId()로 대체한다.
pstmt.setInt(2,member.getMoney());
pstmt.executeUpdate(); //실제 db로 sql 전달
} catch (SQLException e) {
...
}
- con.prepareStatement(sql) : 데이터베이스에 전달할 SQL과 파라미터로 전달할 데이터들을 준비한다.
- pstmt.executeUpdate() : Statement 를 통해 준비된 SQL을 커넥션을 통해 실제 데이터베이스에 전달한다.
PreparedStatement 는 Statement 의 자식 타입인데, ? 를 통한 파라미터 바인딩을 가능하게 해준다.
- 파라미터 바인딩을 하지 않고 단순히 문자열 +연산으로 sql문을 보내면 심각한 문제를 야기할 수 있다.
- 변수 값이 sql 쿼리문으로 대체되기 때문에, 해커가 해당 변수 값에 쿼리 문자열을 넣어 보내면 SQL Injection 공격을 할 수 있다.
catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
- 리소스 정리는 꼭 실행 되어야하기 떄문에 finally 에 작성하자!
- 만약 하지 않을 경우 커넥션이 부족해진다.
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()){ //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) {
...
- resultSet은 iterator와 비슷한, cursor를 통해 데이터를 순서대로 이동시킬 수 있다.
- cursor는 next() 메서드를 통해 다음 데이터로 이동하고, 현재 cursor에서 getType()을 통해 데이터를 꺼낼 수 있다.
수정과 삭제는 등록과 비슷하다. 등록, 수정, 삭제처럼 데이터를 변경하는 쿼리는 executeUpdate() 를 사용하면 된다.
assertThatThrownBy(() -> repository.findById(member.getMemberId()) .isInstanceOf(NoSuchElementException.class);