
JDBC, SQL Mapper, ORM에 대해 알아봤다. JDBC의 기능 3가지를 인지하면서 JDBC를 사용해보자.
java.sql.Connection - 연결java.sql.Statement - SQL을 담은 내용 java.sql.ResultSet - SQL 요청 응답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 = "";
}
데이터베이스에 접속하는데 필요한 정보를 상수로 만들었다.
@Slf4j
public class DBConnectionUtil {
public static Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
log.info("get connection={}, class={}", connection, connection.getClass());
return connection;
}
}
데이터베이스에 연결하기 위해 JDBC가 제공하는 DriverManager.getConnection( )을 사용했다. 이러면 라이브러리에 있는 데이터베이스 드라이버를 찾아서 해당 드라이버가 제공하는 커넥션을 반환해준다.
@Slf4j
class DBConnectionUtilTest {
@Test
void connection() {
Connection connection = DBConnectionUtil.getConnection();
assertThat(connection).isNotNull();
}
}
테스트 코드를 실행해보면

class org.h2.jdbc.JdbcConnection을 확인할 수 있는데 이것이 H2 데이터베이스 드라이버가 제공하는 H2 전용 커넥션이다. 이 커넥션은 JDBC 표준 커넥션 인터페이스인 java.sql.Connection 인터페이스를 구현하고 있다.

그렇다면, 어떻게 데이터베이스 드라이버를 자동으로 찾을 수 있을까?

JDBC가 제공하는 DriverManager는 라이브러리에 등록된 DB 드라이버들을 관리하고, 커넥션을 획득하는 기능을 제공한다.
1) 애플리케이션 로직에서 커넥션이 필요하면 DriverManager.getConnection()을 호출
2) DriverManager는 라이브러리에 등록된 드라이버 목록을 자동으로 인식
3) 찾은 커넥션 구현체가 클라이언트에 반환
@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; //SQL 전달
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() throws SQLException {
return DBConnectionUtil.getConnection();
}
}
PreparedStatement : Statement의 자식 타입인데, ? 를 통한 파라미터 바인딩을 가능하게 해준다.
con.prepareStatement(sql) : 데이터베이스에 전달할 SQL과 파라미터로 전달할 데이터들을 준비한다.
pstmt.setString : 파라미터 바인딩을 통해 ? 에 값을 지정한다.
close(con, pstmt, null) : 쿼리를 실행하고 나면 리소스를 정리해야 한다. 리소스를 정리할 때는 항상 역순으로 해야한다.
@Slf4j
public class MemberRepositoryV0 {
public Member save(Member member) throws SQLException {
//..
}
public Member findById(String memberId) throws SQLException {
String sql = "select * from member where member_id = ?";
Connection con = null; //연결
PreparedStatement pstmt = null; //SQL을 담은 내용
ResultSet rs = null; //SQL 요청 응답
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", e);
throw e;
} finally {
close(con, pstmt, rs);
}
}
private void close(Connection con, Statement stmt, ResultSet rs) {
//..
}
private Connection getConnection() throws SQLException {
//..
}
}
pstmt.executeQuery() : 데이터를 조회할 때 사용 (변경할 때는 executeUpdate( ) 사용)
ResultSet : 다음과 같이 생긴 데이터 구조

ResultSet : 내부에 있는 커서를 이동해서 다음 데이터를 조회할 수 있다.
rs.next() : 커서를 다음으로 이동시킨다. 최초의 커서는 데이터를 가리키고 있지 않기 때문에 rs.next()를 최초 한번은 호출해야 데이터를 조회할 수 있다.
rs.getString("member_id") : 현재 커서가 가리키고 있는 위치의 member_id 데이터를 String 타입으로 반환한다.
public void update(String memberId, int money) throws SQLException {
String sql = "update member set money=? where member_id=?";
Connection con = null; //연결
PreparedStatement pstmt = null; //SQL을 담은 내용
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, memberId);
int resultSize = pstmt.executeUpdate();
log.info("resultSize={}", resultSize);
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
public void delete(String memberId) throws SQLException{
String sql = "delete from member where member_id=?";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
pstmt.executeUpdate();
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null);
}
}
데이터 수정&삭제는 등록과 비슷하다. 조회만 주의하도록 하자.