스프링 JDBC

mark1106·2024년 1월 10일
0

spring

목록 보기
5/5
post-thumbnail

JDBC란?


클라이언트서버를 통해 데이터를 저장, 수정, 조회하면 서버는 데이터베이스를 접근하여 작업을 수행한다. (클라이언트 - 서버 - 데이터베이스)

서버 - 데이터베이스의 동작 과정을 자세히 살펴보자

1. TCP/IP를 통해 커넥션 연결
2. SQL 전달
3. SQL문 수행 후 결과 응답

이 방법의 단점은 각각의 데이터베이스마다 사용법이 다르다는 점이다.

따라서 JDBC(Java Database Connectivity) 표준이 등장한다.

과거에는 각각의 DB마다 사용법이 달라 DB가 바뀌면 애플리케이션 로직도 바뀌어야 했다.

하지만 JDBC 표준 인터페이스를 사용하면 JDBC 인터페이스만 바꾸면 되므로 다음과 같은 문제가 해결된다.

  1. 애플리케이션 로직 변경
  2. 각각 DB마다 커넥션 연결, SQL 전달, 결과 응답 방법을 새로 학습해야 함.

JDBC 접근 기술

최근에는 JDBC를 직접 접근하기보다 편리하게 사용할 수 있는 방법이 있다.

  1. SQL Mapper

장점 : JDBC를 편리하게 사용하도록 도와줌
단점 : SQL문 직접 작성해야함
SQL Mapper Jdbc Template, MyBatis


2. ORM 기술

ORM은(Object Relation Mapping) 객체-관계형 데이터베이스 매핑으로 SQL문을 동적으로 만들어준다. 각각 데이터베이스마다 다른 SQL을 써야하는 문제도 해결

JPA : 자바 진영의 ORM 표준

JPA의 구현체 종류 : 하이버네이트, 이클립스링크

JDBC 접근 기술을 배우기 전에 JDBC의 동작 원리를 알기 위해 직접 JDBC를 사용해보자

데이터베이스 연결

H2 데이터베이스와 연결하기

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 = "";
}
@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);
        }
    }
}

url, username, password를 상수로 등록하고 DriverManager.getConnection(URL, USERNAME, PASSWORD) 를 통해 데이터베이스와 연결할 수 있다.

DriverManager 커넥션 요청 흐름

DriverManger에 사용자가 설정한 정보들(url, username, password)을 전달해주면 DriverManger 라이브러리에 등록된 드라이버 목록에 정보들을 넘겨 연결할 수 있는지 확인한다.

이 때 public static final String URL = "jdbc:h2:tcp://localhost/~/test"; 설정해둔 URL에서 h2는 h2Database에 연결할 수 있다는 것을 의미한다.

연결할 수 있는 데이터베이스가 있다면 커넥션 구현체가 클라이언트에 반환된다.

JDBC를 활용한 CRUD

Member 객체

@Data
public class Member {

    private String memberId;
    private int money;

    public Member() {
    }

    public Member(String memberId, int money) {
        this.memberId = memberId;
        this.money = money;
    }
}

다음과 같은 Member 객체에 대해 CRUD를 진행해보자

등록

public Member save(Member member) throws SQLException {
        String sql = "insert into member(member_id, money) values (?, ?)";

        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, member.getMemberId());
            pstmt.setInt(2, member.getMoney());
            pstmt.executeUpdate();
            return member;
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        }finally {
            close(con, pstmt, null);
        }
    }
private void close(Connection con, Statement stmt, ResultSet rs){

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

        if(stmt != null){
            try {
                stmt.close();
            }catch (SQLException e){
                log.info("error", e);
            }
        }
        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                log.info("error", e);
            }
        }
    }

sql : 데이터베이스에 전달할 sql문 작성

con : 처음에 만들어둔 데이터베이스 커넥션 얻기

pstmt : values (?, ?)에 해당하는 값 설정(1은 첫 번째 인덱스, 2는 두 번째 인덱스)

close : 쿼리 실행 완료하면 리소스 정리(반환하지 않으면 리소스가 쌓이게 됨, 반환은 역순)

조회

public Member findById(String memberId) throws SQLException {
        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()){
                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){
            log.error("db error", e);
            throw e;
        }finally {
            close(con, pstmt, rs);
        }
    }

등록과 비슷하지만 차이점은 resultSet을 통해 데이터를 탐색해야 한다. findById는 하나의 데이터 있는지 조회하지만 모든 데이터가 필요할 때는 while을 사용해서 밑의 예시와 같이 하나하나 탐색하여 member를 찾아야 한다.

ResultSet 결과 예시

cursor가 가리키는 데이터 있음: true 반환
cursor가 리키는 데이터 없음 : false 반환

수정, 삭제

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

        Connection con = null;
        PreparedStatement pstmt = null;

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

    public void delete(String memberId) throws SQLException {
        String sql = "delete from member where member_id=?";

        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, memberId);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        }finally {
            close(con, pstmt, null);
        }
    }

수정과 삭제 또한 등록과 조회와 다르지 않다.

getConnection으로 커넥션을 얻고 pstmt를 활용하여 수정, 삭제할 memberId와 데이터를 설정하고 executeUpdate를 통해 데이터 갱신을 해줄 수 있다.


📚 참고

스프링 DB 1편 - 데이터 접근 핵심 원리 by 김영한

profile
뒤돌아보면 남는 것은 사진, 그리고 기록 뿐

0개의 댓글