JDBC 개발 - 등록

Minseo Kang·2023년 3월 17일
0
post-thumbnail

JDBC로 Member 데이터를 데이터베이스에 관리하는 기능을 개발


주의
H2 데이터베이스 설정 마지막에 있는 테이블과 샘플 데이터 만들기를 통해서 member 테이블을 미리 만들어야 함

drop table member if exists cascade;
create table member (
	member_id varchar(10),
	money integer not null default 0,
	primary key (member_id)
);




01. Member 클래스 작성 - domain 패키지


  • @Data : Getter, Setter, RequiredArgsConstructor, ToString, EqualsAndHashCode, Value 자동으로 생성해줌
  • memberId와 money 멤버 변수를 갖는 단순한 클래스
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;
    }
}



02. MemberRepositoryV0 클래스에 save 메소드 작성 - repository 클래스


  • 회원을 등록하는 메소드
package hello.jdbc.repository;

// JDBC : DriverManger 사용

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

@Slf4j
public class MemberRepositoryV0 {

    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);
            }
        }

    }

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

커넥션 획득

  • DBConnectionUtil 클래스getConnection()을 통해 데이터베이스 커넥션 획득

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.setString(2, member.getMoney()) : SQL의 두번째 ?에 값을 지정, 정수형이므로 setInt
  • pstmt.executeUpdate()
    -Statement를 통해 준비된 SQL을 커넥션을 통해 실제 데이터베이스에 전달
    • executeUpdate()는 영향받은 DB row 수를 int로 반환

리소스 정리

  • 쿼리 실행 후 리소스 정리 필요
  • 리소스 반환 시 항상 역순으로
  • Connection 먼저 획득 후 PreparedStatement 만들었음
  • PreparedStatement 먼저 종료 후 Connection 종료

주의

  • 리소스 정리는 예외가 발생하든, 발생하지 않든 항상 수행되어야 하므로 finally 구문에서 작성
  • 이 부분을 놓치면 커넥션이 끊어지지 않고 계속 유지됨
  • 이것을 리소스 누수라고 함
  • 커넥션 부족으로 인한 장애를 초래할 수 있음

참고

  • PreparedStatementStatement의 자식 타입
  • ?를 통한 파라미터 바인딩
  • SQL Injection 공격 예방하려면 PreparedStatement를 통한 파라미터 바인딩 방식을 사용해야 함



03. MemberRepositoryV0Test(테스트 코드) 작성


  • JDBC로 회원을 데이터베이스에 등록
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 repositoryV0 = new MemberRepositoryV0();

    @Test
    void crud() throws SQLException {
        Member member = new Member("memberV1", 10000);
        repositoryV0.save(member);
        System.out.println(member);
    }
}

테스트 코드 실행 결과




0개의 댓글