들어가며
- 이전 포스트에서 JDBC의 개념, 기술들의 설명에 대해 알아보았다.
- 이번 포스트에서는 코드를 통해 어떻게 사용하는지에 대해 알아본다.
JDBC를 사용한 회원 정보 등록
- 회원 정보를 등록하는 예제를 만들어보자. 여기서는 H2 데이터베이스를 사용하고, 이미 연결이 완료되었다고 가정한다.
- 가장 먼저 회원에 대한 정보를 구현해야 한다. 구현할 회원은 매우 간단한 상태만을 가지고 있다.
- 먼저 회원의 id이다. 이것으로 회원을 유일하게 식별할 수 있다.
- 그 다음이 회원이 가지고 있는 돈 액수이다.
- 이를 클래스로 나타내면 다음과 같다.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member{
private String memberId;
private int money;
}
- 롬복을 사용하여 코드를 간소화하였다. 이 객체를 이후에 member 테이블에 존재하는 데이터를 저장하거나 조회할 때 사용할 것이다.
DB 커넥션 획득
- 데이터베이스에 접속하기 위해서는 접속 URL, 사용자 이름과 비밀번호가 필요하다.
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 = "";
}
- 이제 DB에 접속할 커넥션을 획득하는 클래스를 만들어 보자
import static hello.jdbc.connection.ConnectionConst.*;
public class DBConnectionUtil{
public static Connection getConnection(){
try{
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
return connection;
} catch (SQLException e){
throw new IllegalStateException(e);
}
}
}
- 커넥션을 획득하는 것은 DriverManager의 getConnection을 통해 얻을 수 있다.
- DriverManager는 라이브러리에 등록된 드라이버 목록 중 커넥션을 수행하고 획득할 수 있는 드라이버를 선택하여 커넥션을 획득한다.
- 이 과정은 위의 URL을 통해 수행되는데 DriverManager는 라이브러리에 등록된 각 DB의 드라이버 객체들을 순회하면서 해당 URL을 처리할 수 있는지를 확인한다.
- 해당 URL을 처리할 수 있는 드라이버를 찾는다면 해당 드라이버에 커넥션을 가져오도록 요청하고, 커넥션 구현체가 클라이언트에 반환된다.
Repository 구현
- Repository는 DB와 직접 통신하여 데이터를 주고 받는 계층을 의미한다. DAO라고도 한다.
- 먼저 회원의 데이터를 저장하는 기능을 Repository에 만들어보자.
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.excuteUpdate();
return member;
} catch (SQLException e){
throw e;
} finally{
close(conn, pstmt, null);
}
}
public void close(Connection conn, Statement stmt, ResultSet rs){
if(rs != null){
try{
rs.close();
} catch(SQLException e){
throw e;
}
}
if(stmt != null){
try{
stmt.close();
} catch(SQLException e){
throw e;
}
}
if(conn != null){
try{
conn.close();
} catch(SQLException e){
throw e;
}
}
}
private Connection getConnection(){
return DBConnectionUtil.getConnection();
}
}
- 새로운 것들이 많이 등장했다 빠르게 하나씩 훑어보자.
- conn.prepareStatement(sql)을 통해 데이터베이스에 전달할 SQL과 파라미터로 전달할 데이터들을 준비한다.
- 파라미터로 전달할 데이터들은 PreparedStatement 타입의 pstmt.setString, setInt 등으로 전달한다. 타입에 따라 setXXX로 지정이 되어 있다.
- setXXX의 경우 첫 번째 파라미터는 대응되는 인덱스 번호(위치), 두 번째 파라미터에는 대응되는 값이 들어간다.
- 이후 pstmt.executeUpdate()를 통해 SQL을 실제 DB로 전달한다. executeUpdate() 메서드는 해당 SQL 연산으로 인해 영향받은 행의 수를 반환한다.
- 이후에 모든 동작이 종료되었기 때문에 DB를 연결하는데 든 리소스들을 모두 반환해야 한다. close() 함수를 보면 어떻게 연결을 종료하는지에 대해 구현되어 있다.
- 주의할 점은 저 순서대로 수행해야 한다는 것이다. ResultSet이라는 연산의 결과를 받는 객체부터 먼저 close 되어야 한다. 이후에 statement가 close되어야 하고 최종적으로 Connection이 close되어야 한다.
- 이는 Connection이 먼저 성립되고 나서야 Statement가 성립되고, Statement가 성립되어야 ResultSet도 성립되기 때문에 제거할 때는 역순으로 제거하는 것이다.
- 만약 이러한 제거 방식을 하지 않았을 경우 리소스가 계속 남아있어 장애가 발생할 수 있다.
- 해당 동작이 잘 수행되는지 테스트 코드를 만들어 보자.
동작 테스트 코드
class MemberRepositoryV0Test{
MemberRepositoryV0 memberRepository = new MemberRepositoryV0();
@Test
void crud() throws SQLException{
Member member = new Member("memberA", 10000);
repository.save(member);
}
}
- @Test 어노테이션은 테스트 프레임워크에서 사용하는 어노테이션으로 해당 메서드를 테스트로 동작시키겠다는 것이다. 지금 crud 메서드에서는 새로운 회원을 생성하여 DB에 저장하는 테스트를 수행하고 있다.
- 해당 코드를 수행하고 H2 DB 콘솔에서 SELECT * FROM MEMBER을 수행해보면 새로운 멤버 튜플이 생성된 것을 확인할 수 있다.
- 물론 한 번 더 수행하면 PK 중복 오류가 발생한다. 이는 PRIMARY KEY가 memberId로 지정되어 있기 때문에 발생한다. 따라서 여러 번 수행하려면 DELETE FROM MEMBER을 사용하여 모두 지우고 다시 수행해야 한다.
'Spring & JPA > JDBC' 카테고리의 다른 글
JDBC - JDBC 커넥션 풀 - 커넥션 풀의 개념 (0) | 2023.06.05 |
---|---|
JDBC - JDBC 사용 - 3. 데이터 수정과 삭제 (0) | 2023.06.03 |
JDBC - JDBC 사용 - 2. 데이터 조회 (0) | 2023.06.03 |
JDBC - JDBC의 이해 - 데이터 접근 기술 (0) | 2023.06.03 |
JDBC - JDBC의 이해 - JDBC의 등장 배경 (0) | 2023.06.03 |