Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

JDBC개발-등록 본문

JDBC를 사용해서 회원(Member)데이터를 관리하는 기능을 개발한다.

Member

package hello.jdbc.domain;

import lombok.Data;

@Data
public class Member {

    private String memberId;
    private int money;

    public Member() {
    }

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

 

MemberRepositoryV0 - 회원 등록

Statement: SQL을 그대로 전달

PreparedStatement: Statement상속 받음, 파리미터 바인딩 가능.

package hello.jdbc.repository;

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

import java.sql.*;

/**
 * JDBC - DriverManager 사용
 */
@Slf4j
public class MemberRepositoryV0 {

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

        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, member.getMemberId()); //첫 번째 파라미터 바인딩
            pstmt.setInt(2, member.getMoney()); //두 번째 파라미터 바인딩
            pstmt.executeUpdate(); //쿼리가 db에서 실행됨.
            return member;
        } catch (SQLException e) {
            log.info("db error", e);
            throw e;
        }finally {
            close(conn, pstmt, null);
        }


    }
    //statement : sql그대로 넣음
    //preparedStatement: 파라미터 바인딩 가능., statement상속받음.
    private void close(Connection conn, Statement stmt, ResultSet rs){

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

      if (stmt != null){
          try {
              stmt.close(); //SQLException 발생 가능. 예외 발생해도 아래 conn닫는데 영향 안줌.
          } catch (SQLException e) {
              log.info("error", e);
          }
      }

      if (conn != null){
          try {
              conn.close(); //coonection은 외부 resource(tcp/ip)사용함, 안닫아주면 계속 유지됨..
          } catch (SQLException e) {
              log.info("error", e); // 닫을 때 예외 터지만 할 수 있는게....ㅠ
          }
      }
    }

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

}

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

 

 

save() - SQL 전달

  • sql : 데이터베이스에 전달할 SQL을 정의한다. 
    여기서는 데이터를 등록해야 하므로 insert sql 을 준비
  • con.prepareStatement(sql) : 데이터베이스에 전달할 SQL과 파라미터로 전달할 데이터들을 준비
    -> sql : insert into member(member_id, money) values(?, ?)"
    -> pstmt.setString(1, member.getMemberId()) : SQL의 첫번째 ? 에 값을 지정한다.
     문자이므로 setString 을 사용한다.
    -> pstmt.setInt(2, member.getMoney()) : SQL의 두번째 ? 에 값을 지정한다.
    Int 형 숫자이므로 setInt 를 지정한다.
    -> pstmt.executeUpdate() : Statement 를 통해 준비된 SQL을 커넥션을 통해 실제 데이터베이스에
    전달한다. 참고로 executeUpdate() 은 int 를 반환하는데 영향받은 DB row 수를 반환한다. 여기서는
    하나의 row를 등록했으므로 1을 반환한다.

 

리소스 정리

  • 쿼리를 실행하고 나면 리소스를 정리해야 한다. 
  • 리소스 정리는 항상 finally에서 하도록 하자. try문에서 만약 리소스를 정리한다고 했을 때, 앞의 코드 진행 중 Exception이 발생한다면 catch로 넘어가게된다. 이렇게 되면 close 를 호출할 수 없게 되기 때문.
  • 여기서는 Connection , PreparedStatement 를 사용했다.  리소스를 정리할 때는 항상 역순으로 해야한다. 
    Connection 을 먼저 획득하고 Connection 을 통해 PreparedStatement 를 만들었기 때문에 
    리소스를 반환할 때는 PreparedStatement 를 먼저 종료하고, 그 다음에 Connection 을 종료하면 된다. 
    참고로 여기서 사용하지 않은 ResultSet 은 결과를 조회할 때 사용한다. 

주의 

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

참고 

PreparedStatement 는 Statement 의 자식 타입인데, ? 를 통한 파라미터 바인딩을 가능하게 해준다.
참고로 SQL Injection 공격을 예방하려면 PreparedStatement 를 통한 파라미터 바인딩 방식을 사용해야 한다
파라미터 바인딩을 사용하며 단순히 데이터로 취급된다. 하지만 PreparedStatement를 사용하지 않는 경우에 파라미터에 sql 구문이 들어오면 sql로 취급될 수 있다.

 

MemberRepositoryV0Test - 회원 등록

package hello.jdbc.repository;

import hello.jdbc.domain.Member;
import org.junit.jupiter.api.Test;

import java.sql.SQLException;

import static org.junit.jupiter.api.Assertions.*;

class MemberRepositoryV0Test {

    MemberRepositoryV0 repository = new MemberRepositoryV0();


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

오 member가 잘 들어갔다. 코드를 다시 한 번 실행해 보자.

다음과 같은 예외가 발생한다.

org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation

이유는 간단하다, 현재 PK가 MEMBER_ID인데 PK가 중복되었기 때문.

'인프런 > [인프런] 스프링 DB 1편 - 데이터 접근 핵심 원리' 카테고리의 다른 글

JDBC 개발 - 수정, 삭제  (0) 2022.05.28
JDBC 개발-조회  (0) 2022.05.23
DB 연결  (0) 2022.05.22
JDBC와 최신 데이터 접근 기술  (0) 2022.05.22
JDBC 이해  (0) 2022.05.22
Comments