[되새김질] 스프링 DB 1편 - JDBC 이해

jeyong·2023년 8월 26일
0

해당 게시물은 인프런 "스프링 DB 1편 - 데이터 접근 핵심 원리" 강의를 참고하여 작성한 글 입니다.

1. JDBC 이해

  • 클라이언트가 직접 데이터베이스에 접근하지 않고, 애플리케이션 서버를 거쳐 DB의 데이터를 받아오게 된다.
  • 애플리케이션 서버는 DB서버와 연결을 해두고, SQL과 같은 DB 구문을 통해 요청을 한 데이터를 받아오게 된다.

과거의 수많은 DB들은 모두 사용법이 달랐고 애플리케이션 서버는 DB의 구현법에 의존할 수밖에 없었다.
-> 이를 해결하기 위해 JDBC 자바 표준 인터페이스가 등장한다.

  • JDBC 인터페이스를 각 DataBase 회사들이 구현체인 라이브러를 만들어 제공한다.
    - 이 라이브러리를 JDBC 드라이버라고 한다.

  • 서버 로직의 변경 없이 어떤 DB로든( JDBC 드라이버를 제공하는) 교체할 수 있다.

2. JDBC와 최신 데이터 접근 기술

  • JDBC는 사용하기 복잡해서 최근에는 JDBC를 직접 사용하기보다는 편리하게 사용할 수 있는 기술을 사용한다

SQLMapper

  • SQL 응답 결과를 객체로 편리하게 변환해주고 JDBC의 반복 코드를 제거해준다
  • 하지만, 개발자가 SQL을 직접 작성해야해서 귀찮다.. ( 나중에 해봄 )
  • 대표적으로 스프링 Jdbc Template와 MyBatis가 있다.

ORM

  • 객체를 DB의 테이블과 매핑하여 SQL을 자동으로 만들어 DB와 소통한다.
  • ORM은 사용하는 DB를 인식해서 DB에 맞는 SQL문을 만들어 주기 때문에 DB마다 다른 SQL문을 사용할 필요가 없다.
  • 대표적으로 JPA라는 자바 표준 인터페이스를 하이퍼네이트와 이클립스링크가 구현하고 있다.

3. 데이터베이스 연결

Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);

java.sql.Connection은 인터페이스로, 각 DB의 구현체를 DriverManager.getConnection(URL, USERNAME, PASSWORD)로 가져올 수 있다.

이렇게 하면 라이브러리에 있는 데이터베이스 드라이버를 찾아서 해당 드라이버가 제공하는 커넥션을 반환해준다.

getConnection은 내가 어떤 데이터 베이스를 원하는지 어떻게 알까?

  • DriverManager는 JDBC가 제공하는 기능으로, 라이브러리에 등록된 드라이버 목록에서 커넥션을 요청한다.
  • 여러 DB 드라이버가 있다면 순서대로 URL, USERNAME, PASSWORD를 통해 접근해보고 접근이 되면, 구현체가 반환된다.

4. JDBC 개발 - 등록

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

PreparedStatement 는 Statement 의 자식 타입인데, ? 를 통한 파라미터 바인딩을 가능하게 해준다.

  • 파라미터 바인딩을 하지 않고 단순히 문자열 +연산으로 sql문을 보내면 심각한 문제를 야기할 수 있다.
  • 변수 값이 sql 쿼리문으로 대체되기 때문에, 해커가 해당 변수 값에 쿼리 문자열을 넣어 보내면 SQL Injection 공격을 할 수 있다.

하지만, 해당 코드만으로 끝이아니다. 왜냐하면 리소스 정리를 하지 않았기 때문이다.

catch (SQLException e) { 
 log.error("db error", e);
 throw e;
 } finally {
 close(con, pstmt, null);
 }
 }
  • 리소스 정리는 꼭 실행 되어야하기 떄문에 finally 에 작성하자!
  • 만약 하지 않을 경우 커넥션이 부족해진다.

5. JDBC 개발 - 조회

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) {
...

executeUpdate() vs executeQuery()

  • PreparedStatement.executeUpdate()는 값을 DB에 반영할 때 사용한다.
  • PreparedStatement.executeQuery()는 SELECT와 같이 값을 가져와야 할 때 사용하면 ResultSet 객체로 데이터를 반환받을 수 있다.

ResultSet

  • executeQuery() 는 결과를 ResultSet 에 담아서 반환한다.

  • resultSet은 iterator와 비슷한, cursor를 통해 데이터를 순서대로 이동시킬 수 있다.
  • cursor는 next() 메서드를 통해 다음 데이터로 이동하고, 현재 cursor에서 getType()을 통해 데이터를 꺼낼 수 있다.

6. JDBC 개발 - 수정, 삭제

수정과 삭제는 등록과 비슷하다. 등록, 수정, 삭제처럼 데이터를 변경하는 쿼리는 executeUpdate() 를 사용하면 된다.

-> 따라서 CRUD만 메서드 별로 구현하는데도 굉장히 많은 중복이 발생하고 있다.

7. + α

@Slf4j

  • 다양한 로깅 프레임 워크에 대한 추상화(인터페이스) 역할을 하는 라이브러이다.

@Slf4j 공부한 곳 링크

assertThatThrownBy

assertThatThrownBy(() -> repository.findById(member.getMemberId()) .isInstanceOf(NoSuchElementException.class);
  • 익셉션이 발생하는 케이스를 테스트할 때 사용한다.

assertThatThrownBy 공부한 곳 링크

profile
천천히 잊어가기

0개의 댓글