728x90
Schema를 작성하여 Table 데이터를 초기화 하자
drop table member if exists cascade;
create table member (
member_id varchar(10),
money integer not null default 0,
primary key (member_id)
);
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;
}
}
MemberRepositoryV0
/**
* JDBC - DriverManager 사용
*/
@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(); //Exception 발생 시, 어떻게 될까? con.close 가 호출X
} catch (SQLException e) {
log.error("error", e);
}
}
if(stmt != null) {
try {
stmt.close(); //Exception 발생 시, 어떻게 될까? con.close 가 호출X
} catch (SQLException e) {
log.error("error", e);
}
}
if(con != null) {
try {
con.close();
} catch (SQLException e) {
log.error("error", e);
}
}
}
private Connection getConnection() {
return DBConnectionUtil.getConnection();
}
}
- 커낵션 획득 : getConnection()
- 이전에 만들어둔 DBConnectionUtil를 통해서 데이터베이스 커넥션을 획득한다
- save()
- 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을 반환한다.
- 리소스 정리
- 쿼리를 실행하고 나면 리소스를 정리해야 한다. 여기서는 Connection , PreparedStatement를 사용했다. 리소스를 정리할 때는 항상 역순으로 해야한다. Connection을 먼저 획득하고 Connection 을 통해 PreparedStatement 를 만들었기 때문에 리소스를 반환할 때는 PreparedStatement 를 먼저 종료하고, 그 다음에 Connection 을 종료하면 된다. 참고로 여기서 사용하지 않은 ResultSet 은 결과를 조회할 때 사용한다.
주의
리소스 정리는 꼭! 해주어야 한다. 따라서 예외가 발생하든, 하지 않든 항상 수행되어야 하므로 finally 구문에 주의해서 작성해야 한다. 만약 이 부분을 놓치게 되면 커넥션이 끊어지지 않고 계속 유지되는 문제가 발생할 수 있다. 이런 것을 리소스 누수라고 하는데, 결과적으로 커넥션 부족으로 장애가 발생할 수 있다
주의
PreparedStatement는 Statement의 자식 타입인데,? 를 통한 파라미터 바인딩을 가능하게 해 준다.
참고로 SQL Injection 공격을 예방하려면 PreparedStatement를 통한 파라미터 바인딩 방식을 사용해야 한다
Test
class MemberRepositoryV0Test {
MemberRepositoryV0 repository = new MemberRepositoryV0();
@Test
void crud() throws SQLException {
Member member = new Member("memberV1", 10000);
repository.save(member);
}
}
- 한번 실행 시 데이터를 잘 저장하는 것을 확인할 수 있다.
- 두 번 실행 시 PK 중복 오류가 발생한다(delete 쿼리를 사용해서 삭제한 뒤 실행하자)
728x90
'스프링 DB 1편(데이터 접근 핵심 원리)' 카테고리의 다른 글
| Ch01. JDBC 이해 - JDBC 개발(수정, 삭제) (0) | 2022.04.23 |
|---|---|
| Ch01. JDBC 이해 - JDBC 개발(조회) (0) | 2022.04.23 |
| Ch01. JDBC 이해 - 데이터베이스 연결 (0) | 2022.04.23 |
| Ch01. JDBC 이해 - JDBC와 최신 데이터 접근 기술 (0) | 2022.04.23 |
| Ch01. JDBC 이해 - JDBC 이해 (0) | 2022.04.23 |