JDBC 실습 해보기
- 커넥션 연결
- SQL 전달
- 결과 응답 받아오기
우선 이 3가지 단계를 생각하자
개발자는 모든 DB 사용 3단계를 공부할 필요가 없다. 이미 DB 회사마다 개발하여 JDBC 표준 인터페이스에 맞게 구현 해놓은 드라이버들을 JDBC 표준 인터페이스에 끼운 다음 사용하는 것이 중요하다. 이를 기억하자!!
커넥션 연결, SQL 전달, 결과 응답 반환을 할 수 있도록 도와주는 코드들이 모여 있는 라이브러리들을 드라이버라고 한다
그럼 지금 이 글을 쓰면서 DB 사용을 하기 위해 내가 알아야 할 것을 생각해보자.
- DB 라이브러리를 어떻게 가져와서 JDBC 표준 인터페이스에 끼워 넣어줄 것인가?
- 이렇게 표준 인터페이스에 끼워넣어서 사용하면 뭐가 좋을까?
- 개발자는 표준 인터페이스의 사용법만 알면 DB 기술을 변경하여야 할 때 구현되어 있는 라이브러리만 교체하면 되어서 서버의 코드를 그대로 유지할 수 있다.
- 사용법만 알면 된다. (모든 DB 연결 기술을 공부할 필요가 없다.)
지금 실습할 데이터 베이스 연결 방법은 JDBC 기술을 사용할 것이고, 사용할 데이터 베이스는 H2 데이터 베이스를 사용할 것이다.
항상 연결전에는 데이터 베이스 서버를 먼저 실행 해두자.
애플리케이션 서버 입장
내가 DB에 연결하기 위해서는 DB 연결 정보가 필요하니까 우선 DB연결 정보를 어디서 가지고 있어야겠지?..
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랑 연결할 때 넘길 DB서버의 정보
위 정보를 가지고 연결을 시작하는 로직을 담은 메서드를 정의하기
public class DBConnectionUtil {
public static Connection getConnection() {
try {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD); // JDBC가 제공하는 라이브러리
// DB 드라이버를 찾아서 해당 드라이버가 제공하는 커넥션을 반환해준다.
log.info("connection={}, class={}", connection, connection.getClass());
return connection;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
JDBC가 제공하는 DriverManager 의 .getConnection({DB 연결 정보 매개변수로 넣기}) 사용해야 한다.
그러면 DB 사용 3단계 중 1단계에 해당하는 커넥션 연결 얻기를 성공했다.
- [x] 커넥션 연결
- [ ] SQL 전달
- [ ] 결과 응답 받아오기
커넥션 연결을 성공하기 위해서는 데이터 베이스 서버가 실행 중이어야 한다.
SQL 전달을 해보기.
회원을 저장하는 로직을 가진 save() 메서드를 한번 살펴보자
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); // 전달할 sql을 넣어준다.
// ?에 바인딩하기
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate(); // 실행할 sql 전달
return member;
} catch (SQLException e) {
log.error("db error");
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("rs close error");
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
log.info("stmt close error");
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
log.info("con close error");
}
}
}
얻은 커넥션에 데이터 베이스에 전달할 sql을 pstmt에 넣어주고
?에 해당하는 데이터에 바인딩을 해준다.
pstmt.executeUpdate(): Statement에 바인딩이 완료된 sql을 커넥션을 통해 db로 전달하게 된다.
close(con, pstmt, null): DB 리소스를 정리하는 메서드. 예외가 발생하든 하지 않든 꼭 로직 마지막에 수행되어야 하는 메서드
Statement는 ?를 사용하여 파라미터 바인딩을 도와주는 클래스이며 PreparedStatement는 Statement를 상속받은 클래스이다. SQL Injection 공격을 예방하기 위해서 ?를 통한 파라미터 바인딩 방식을 사용하는 것이 좋다.
그럼 SQL 전달까지 했고 실제 DB에 반영까지 해볼 수 있었다.
- [x] 커넥션 연결
- [x] SQL 전달
- [ ] 결과 응답 받아오기
결과 응답을 받아오는 부분은 조회 메서드를 살펴보면 알 수 있다.
public Member findById(String memberId) throws SQLException {
String sql = "select * from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
rs = pstmt.executeQuery(); // 결과 응답을 받을 때 사용하는 메서드
if (rs.next()) {
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId=" + memberId);
}
} catch (SQLException e) {
log.error("db error");
throw e;
} finally {
close(con, pstmt, rs);
}
}
조회를 하면 DB에서 조회한 조회 결과를 저장해야 한다. 이를 ResultSet에 담아서 반환하게 된다.
또한 이 결과를 사용할 때에 rs.next()를 통해 읽게 되는데
- 최초에 한번을 호출하여 제일 첫 행으로 커서를 이동시킨다. 그런 다음 rs 데이터의 커서가 가리키고 있는 “member_id”에 해당하는 데이터를 String, “money”에 해당하는 데이터를 int타입으로 가져온다.
이외 회원 정보를 수정, 삭제를 DB에 반영하는 실습과 test를 진행해보았다.
github 실습 코드
이제 결과응답을 반환받는 실습까지 해보았다.
- [x] 커넥션 연결
- [x] SQL 전달
- [x] 결과 응답 받아오기
정리
- DB 사용은 3단계 커넥션 연결 → SQL 전달 → 결과 응답 활용하기로 이루어져 있다.
- 로직마다 동일한 작업 코드가 반복된다.