[Spring] JDBC 관련

JJoSuk·2023년 6월 13일
0

본 프로젝트 자료는 김영한님의 스프링 DB 1편 - 데이터 접근 핵심 원리을 참고 제작됐음을 알립니다.

JDBC

데이터를 담기 위해 만들어진 데이터베이스라 보면 된다.

동작원리

클라이언트 -> 애플리케이션 서버 -> DB

애플리케이션 서버 -> DB - 일반적인 사용법

  1. TCP/IP 를 사용해서 커넥션 연결.
  2. DB 가 이해 할 수 있는 SQL 을 연결된 커넥션 통해 DB 전달
  3. DB 는 애플리케이션 서버에서 받은 SQL 을 수행하고 결과값을 다시 응답.

다른 종류의 DB 사용법

전 DB 와 다른 동작 방식인걸 확인 할 수 있다. 여기서 DB 는 위 2개 말고 더 많은 관계형 데이터베이스가 있다.

여기서 볼 수 있는 문제점

  • 다양한 데이터베이스가 있다보니 불행하게도 전부 통일된 방식이 아닌 데이터베이스가 바뀔 때 마다 수행하는 코드도 바꿔야 한다.
  • 그렇다는 말은 여러 데이터베이스가 있다는 것은 여러 DB 를 수행할 수 있는 방법을 전부 배워야 한다.

이러면 문제점들이 생각보다 일에 대한 효율이 좋지 않아, 여기서 등장한 것이 JDBC 라는 자바 표준이다.

JDBC 표준 인터페이스

이 자바 표준이 어떻게 개발자에게 편리한 기능을 제공하는지 확인해보자.

대표적으로 다음 3가지 기능을 표준 인터페이스로 정의해서 제공해준다.

  1. java.sql.Connection - 연결
  2. java.sql.Statement - SQL을 담은 내용
  3. java.sql.ResultSet - SQL 요청 응답

그렇다고 이 3가지로 DB 가 동작하는게 아니다. 말 그대로 DB 를 좀 더 편리하게 연결 할 수 있게 만들어 주는 기능들이지 핵심인 DB 가 없다.

이 DB 들은 각 DB 벤더에서 라이브러리 형태로 제공이 되는데(JDBC 드라이버) 위 3가지 기능과 드라이버를 사용해 편리하게 DB 를 설계하면 된다.

DB 설계 예시

MySQL 드라이버 사용

Oracle 드라이버 사용

요론 식으로 동작한다. 표준이 등장하면서 편리해진게 눈에 보인다.

정리

표준이 등장하면서 2가지 문제점 해결

  1. 다른 종류의 데이터베이스로 변경해야 할 때 사용 코드도 같이 변경해야하는 점 해결.
  2. 각 데이터베이스 마다 커넥션 연결, SQL 전달, 응답 받는 방법 등 새로 학습해야 하는 점 해결.

표준이 등장하면서 남아있는 한계 점

  • 회사마다 기술이 다르다 보니 동작하는 원리가 달라, 아무리 공통화를 했다고 해도 일반적은 부분만 공통화 작업만 했다. 그래서 실질적으로 바뀔 때 SQL 을 해당 데이터베이스에 맞게 변경 작업을 해줘야 한다.

JDBC와 최신 데이터 접근 기술

JDBC는 1997년에 출시될 정도로 오래된 기술이다 보니 다양한 응용 기술이 존재하는데, 그 중 대표적인게 SQL Mapper 와 ORM 기술이다.

JDBC 직접 사용를 직접 사용했을 때는

SQL Mapper 는

이렇게 진행했을 때의 장점

  • SQL 응답 결과를 객체로 편리하게 변환해준다.
  • JDBC의 반복 코드를 제거해준다.

하지만 개발자가 직접 SQL 문을 작성해야 하는 점이 존재한다.

SQL Mapper 의 대표 기술 : JdbcTemplate, MyBatis

이제 ORM 기술에 대해 알아보도록 하자.

ORM 기술

ORM 은 객체를 관계형 데이터베이스 테이블과 매핑해주는 기술을 가지고 있다보니, 개발자들이 직접 SQL 을 작성하지 않고 ORM 기술이 개발자 대신 SQL을 동적으로 만들어 준다.

ORM 기술 의 대표 기술 : JPA, 하이버네이트, 이클립스링크

이 두 기술의 각 장단점을 알아보도록 하자.

SQL Mapper vs ORM 기술

  • SQL Mapper 은 직접 SQL문만 작성하면 나머지 번거로운 일은 SQL Mapper 가 대신 처리해준다. 난이도가 비교적 쉽다.

  • ORM 기술은 SQL 을 작성하지 않아도 되어서 비교적 쉬워보이지만, 난이도가 있어 사용할꺼면 깊이 있게 배워야 한다.


데이터베이스 연결

이제 H2 데이터베이스를 먼저 실행하고 애플리케이션과 연결해보자.

ConnectionConst - 생성

데이터베이스에 접속 할 수 있게 기본 정보를 입력.

DBConnectionUtil - 생성

일단 데이터베이스에 연결하려면 DriverManager.getConnection(..) 사용해야 한다.

이제 학습용 테스트 코드를 작성해서 제대로 연결이 됐는지 확인해보자.

DBConnectionUtilTest - 테스트 코드 생성

H2 데이터베이스가 실행된 상태여야 실행하는제 에러코드가 발생하지 않는다.

결과 로그

내용 중에 class=class org.h2.jdbc.JdbcConnection 를 확인 할 수 있는데, 이 부분이 H2 데이터베이스 전용 커넥션이다.

이 커넥션도 JDBC 표준 커넥션에 구현되어 있다.

JDBC DriverManager 연결 이해

지금 실행하면서 어떻게 동작이 되는지 그 원리를 파악해 보자.

JDBC 커넥션 인터페이스와 구현

  • JDBC는 java.sql.Connection 표준 커넥션 인터페이스를 정의
  • H2 데이터베이스 드라이버는 JDBC Connection 인터페이스를 구현한 org.h2.jdbc.JdbcConnection 구현체를 제공

DriverManager 커넥션 요청 흐름

가운데 있는 DriverManager 가 라이브러리 에 등록된 DB 드라이버를 관리하고, 커넥션을 획득하는데 도움을 준다.

  1. 애플리케이션 로직에서 커넥션이 필요하면 DriverManager.getConnection() 을 호출.
  2. 등록된 DB 드라이버를 목록에서 자동으로 인식, 아래 정보를 넘겨서 커넥션을 획득할 수 있는지 확인.
  • URL: 예) jdbc:h2:tcp://localhost/~/test
  • 이름, 비밀번호 등 접속에 필요한 추가 정보
  • 예를 들어 H2 드라이버가 사용되어 jdbc:h2 를 인식된다면 본인이 처리할 수 있어 알아서 처리하지만, 오라클 드라이버를 사용하는데 jdbc:h2 를 인식한다면 본인이 처리할 수 없어 다음 드라이버에게 순서가 넘어간다.

JDBC 개발 - 등록

JDBC 를 사용해서 애플리케이션을 개발해보자.

여기서는 JDBC 를 사용해서 회원(Member) 데이터를 데이터베이스에 관리하는 기능을 개발해보자.

H2 데이터베이스는 항상 실행시켜야 한다! 그리고 Member 를 테스트 하는거니 테이블도 생성해야 한다!

H2 데이터베이스

Member - 생성

테이블과 일치하게 회원의 ID와 해당 회원이 소지한 금액을 표현했다.

MemberRepositoryV0 - 생성

회원 등록해보자.

리소스 정리

쿼리를 실행하고 나면 리소스를 정리해야 한다. 여기서는 Connection , PreparedStatement 를 사용했다.
1. 생성할 때는 Connection 을 먼저 획득하고 Connection 을 통해 PreparedStatement 생성.
2. 반환할 때는 PreparedStatement 를 먼저 종료하고, 그 다음에 Connection 을 종료.

주의
리소스 정리는 꼭! 해주어야 한다. 따라서 예외가 발생하든, 하지 않든 항상 수행되어야 하므로 finally
구문에 주의해서 작성해야한다. 만약 이 부분을 놓치게 되면 커넥션이 끊어지지 않고 계속 유지되는 문제가 발생할 수 있다. 이런 것을 리소스 누수라고 하는데, 결과적으로 커넥션 부족으로 장애가 발생할 수 있다.

이제 테스트 코드로 JDBC 회원 등록이 되는지 테스트 해보자.

MemberRepositoryV0Test - 테스트 생성

memberV0 등록

memberV1 등록

성공적으로 등록된걸 확인 할 수 있다.


JDBC 개발 - 조회

JDBC 조회하는 기능을 개발해보자.

MemberRepositoryV0 - 추가

회원 조회 추가

ResultSet 이란?

  • 예를 들어서 select member_id, money 라고 지정하면 member_id , money 라는 이름으로 데이터가 저장한다.
  • select * 을 사용하면 테이블의 모든 컬럼을 다 지정한다.
  • rs.next()을 cursor 를 최초 한번은 호출해야 데이터를 조회할 수 있다.
    • rs.next() 의 결과가 true 면 커서의 이동 결과 데이터가 있다는 뜻이다.
    • rs.next() 의 결과가 false 면 더이상 커서가 가리키는 데이터가 없다는 뜻이다.
  • rs.getString("member_id") : 현재 커서가 가리키고 있는 위치의 member_id 데이터를 String 타입으로 반환한다.
  • rs.getInt("money") : 현재 커서가 가리키고 있는 위치의 money 데이터를 int 타입으로 반환한다.

ResultSet 결과 예시

findById() 에서는 회원 하나를 조회하는 것이 목적이기에 while 대신 if 를 사용한다.

MemberRepositoryV0Test - 추가 및 실행

회원 조회 추가 및 실행 결과 확인

회원을 등록하고 그 결과를 바로 조회할 수 있다.
참고로 실행 결과에 member 객체의 참조 값이 아니라 실제 데이터가 보이는 이유는 롬복의 @Data 가 toString() 을 적절히 오버라이딩 해서 보여주기 때문이다.


JDBC 개발 - 수정, 삭제

수정과 삭제도 등록해보자. 우선 데이터 수정

MemberRepositoryV0 - 추가

회원 수정 추가

executeUpdate() 는 쿼리를 실행하고 영향받은 row수를 반환한다. 여기서는 하나의 데이터만 변경하기 때문에 결과로 1이 반환된다. 만약 회원이 100명이고, 모든 회원의 데이터를 한번에 수정하는 update sql 을 실행하면 결과는 100이 된다.

MemberRepositoryV0Test - 추가

회원 수정 추가

이제 회원 데이터가 10000 -> 20000으로 수정됐는지 확인해보자.

실행했을 때 정상적으로 10000 에서 20000으로 수정된 것을 확인 할 수 있다.

다음은 회원 삭제를 해보자.

회원 삭제

MemberRepositoryV0 - 추가

회원 삭제 추가

쿼리만 바뀌고 나머지는 별 차이 없다.

MemberRepositoryV0Test - 추가

회원 삭제 추가

회원을 삭제한 다음 findById() 를 통해서 조회한다. 회원이 없기 때문에 NoSuchElementException 이 발생한다. assertThatThrownBy 는 해당 예외가 발생해야 검증에 성공한다.

profile
안녕하세요

0개의 댓글