1. JDBC의 이해

ChoiJaewoo·2026년 2월 23일

Spring DB 1편

목록 보기
1/6
  • 프로젝트 생성
  • H2 데이터베이스 설정
  • JDBC 이해
  • JDBC 와 최신 데이터 접근 기술
  • 데이터베이스 연결
  • JDBC 개발 - 등록
  • JDBC 개발 - 조회
  • JDBC 개발 - 수정, 삭제

프로젝트 생성, H2 데이터베이스 설정

시작하기 전,

  1. Spring initializer - Lombok, H2 Database, JDBC API 추가하여 빌드.
  2. H2 database 설치.

JDBC 이해

예전, DB 마다 커넥션을 연결하는 방법, SQL을 전달하는 방법과 그 결과를 응답 받는 방법이 모두 달랐다.

문제 → DB 가 바뀌면, 코드도 바꿔야 한다.

문제 → 개발자가 각각의 DB 사용법을 모두 공부해야 한다.

이러한 문제를 해결하기 위해 JDBC → 표준 인터페이스가 (자바 표준) 등장!

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

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

JDBC와 최신 데이터 접근 기술

→ JDBC 를 직접 사용하면 너무 복잡하다.

  • SQL Mapper
    • 장점 : JDBC를 편리하게 사용하도록 도와준다.
      • SQL 응답 결과를 객체로 편리하게 변환해준다.
      • JDBC 의 반복 코드를 제거해준다
    • 단점 : 개발자가 직접 SQL문을 작성해야 한다.
    • 대표 기술 : 스프링 JdbcTemplate, MyBatis
  • ORM 기술
    • ORM 은 객체를 관계형DB 테이블과 매핑해주는 기술이다 → 직접 SQL을 작성하지 않고, ORM 기술이 개발자 대신 SQL을 동적으로 만들어 실행해준다.
    • DBMS 마다 다른 SQL을 사용하는 문제도 중간에서 해결해준다
    • 대표 기술 : JPA, 하이버네이트
    • JPA → 자바 ORM 표준 인터페이스, 이것을 구현한 것으로 하이버네이트 이다.

→ 그러면 왜 JDBC 를 배워야 할까?

: 위의 기술들 모두 JDBC를 사용한다. 따라서 직접 JDBC를 사용하지 않더라도, JDBC가 어떻게 동작하는지 기본 원리를 알아두어야 한다.

: 문제가 발생했을 때 근본적인 문제를 찾아서 해결할 수 있다.

데이터베이스 연결

애플리케이션과 데이터베이스를 연결해보자.

  • H2 데이터베이스 서버를 실행해두고 시작.

ConnectionConst

public abstract class ConnectionConst {
    public static final String URL = "jdbc:h2:tcp://localhost/~/test";
    public static final String USERNAME = "sa";
    public static final String PASSWORD = "";
}

DBConnectionUtil

@Slf4j
public class DBConnectionUtil {
    public static Connection getConnection() {
        try {
            Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            log.info("get connection={}, class={}", connection, connection.getClass());
            return connection;
        } catch (SQLException e) {
            throw new IllegalStateException(e);
        }
    }
}

Connection → JDBC 표준 인터페이스가 제공하는 바로 그 커넥션!

JDBC Driver Manager 연결 이해

  1. 애플리케이션 로직에서 커넥션이 필요하면, DriverManager.getConnection()을 호출한다.

    → URL, USERNAME, PASSWORD

  2. DriverManager는 라이브러리에 등록된 드라이버 목록을 자동으로 인식한다.

  3. 찾은 커넥션 구현체가 애플리케이션 로직으로 반환된다.

  4. 받은 해당 DB의 커넥션 구현체를 사용하면 된다!

JDBC 개발 - 등록

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

JDBC 개발 - CRUD

package hello.jdbc.repository;

import hello.jdbc.connection.DBConnectionUtil;
import hello.jdbc.domain.Member;
import lombok.extern.slf4j.Slf4j;

import java.sql.*;
import java.util.NoSuchElementException;

/**
 * JDBC - DriverManager를 사용해서 저장해보기
 */
@Slf4j
public class MemberRepositoryV0 {
    public Member save(Member member) throws SQLException {
        String sql = "insert into member(member_id, money) values (?,?)";

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, member.getMemberId());
            preparedStatement.setInt(2, member.getMoney());
            preparedStatement.executeUpdate();
            return member;
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        } finally {
            close(connection, preparedStatement, null);
        }
    }

    public Member findById(String memberId) throws SQLException {
        String sql = "select * from member where member_id = ?";

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, memberId);

            resultSet = preparedStatement.executeQuery();

            if (resultSet.next()) {
                Member member = new Member();
                member.setMemberId(resultSet.getString("member_id"));
                member.setMoney(resultSet.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(connection, preparedStatement, resultSet);
        }
    }

    public void update(String memberId, int money) throws SQLException {
        String sql = "update member set money=? where member_id=?";

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, money);
            preparedStatement.setString(2, memberId);
            int resultSize = preparedStatement.executeUpdate();
            log.info("resultSize={}", resultSize);
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        } finally {
            close(connection, preparedStatement, null);
        }
    }

    public void delete(String memberId) {
        String sql = "delete from member where member_id = ?";
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try{
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, memberId);
            preparedStatement.executeUpdate();
            log.info("delete memberId={}", memberId);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private void close(Connection connection, Statement statement, ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                log.error("error", e);
            }
        }
        if (connection != null) {

            try {
                connection.close();
            } catch (SQLException e) {
                log.error("error", e);
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                log.error("error", e);
            }
        }

    }

    private Connection getConnection() {
        return DBConnectionUtil.getConnection();
    }
}

TestCode

package hello.jdbc.repository;

import hello.jdbc.domain.Member;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import java.sql.SQLException;
import java.util.NoSuchElementException;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.*;

@Slf4j
class MemberRepositoryV0Test {

    MemberRepositoryV0 repository = new MemberRepositoryV0();

    @Test
    void crud() throws SQLException {
        Member memberV0 = new Member("memberV100", 10000);
        repository.save(memberV0);

        //FindById
        Member findMember = repository.findById(memberV0.getMemberId());
        log.info("findMember = {}", findMember);

        log.info("member != findMember {}", memberV0 == findMember);
        assertThat(findMember).isEqualTo(memberV0);

        //update money 10000 -> 20000
        repository.update(memberV0.getMemberId(), 20000);
        Member updatedMember = repository.findById(memberV0.getMemberId());
        assertThat(updatedMember.getMoney()).isEqualTo(20000);

        //delete
        repository.delete(memberV0.getMemberId());

        //등록 했다가 바로 삭제하는 것이기 때문에 볼 수 없다.
        //그럼 검증을 어떻게 해야 하는가?
        assertThatThrownBy(() -> repository.findById(memberV0.getMemberId())).isInstanceOf(NoSuchElementException.class);

    }
}
  • 커넥션 획득
    • getConnection() : DBConnectionUtil 을 통해 데이터베이스 커넥션을 획득한다.

0개의 댓글