Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- Greedy
- QueryDSL
- AOP
- 알고리즘
- db
- Proxy
- Exception
- transaction
- Thymeleaf
- 그리디
- Servlet
- Spring Boot
- JDBC
- 백준
- SpringBoot
- kotlin
- springdatajpa
- 스프링 핵심 기능
- JPQL
- pointcut
- 자바
- 스프링
- jpa
- 인프런
- 스프링 핵심 원리
- 김영한
- http
- spring
- java
- Android
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