1장에서 초난감 DAO 코드에 DI를 적용하여 관심이 다른 코드를 다양한 방법으로 분리하고, 확장과 변경에 용이하게 대응할 수 있는 설계구조로 개선하는 작업을 진행했다.
개방 폐쇄원칙
템플릿
이전장에서 UserDao 코드의 DB 연결과 관련된 여러가지 개선 작업을 했지만, 아직 예외상황에 대한 처리부분이 부족하다.
DB 커넥션이라는 제한적인 리소스를 공유해 사용하는 서버에서 동작하는 JDBC 코드가 지켜야할 원칙
JDBC 수정 기능의 예외 처리 코드
public void deleteAll() throws SQLException {
Connection c = this.dataSource.getConnection();
// 여기서 예외가 발생하면, 공유 리소스인 ps, c를 반환하지 않고 메소드 실행이 종료된다.
PreparedStatement ps = c.prepareStatement("delete from users");
ps.executeUpdate();
ps.close();
c.close();
}
위 코드에 try/catch/finally 구문을 적용해보자.
public void deleteAll() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
try {
c = this.dataSource.getConnection();
ps = c.prepareStatement("delete from users");
ps.executeUpdate();
} catch (SQLException e) {
throw e;
} finally {
if (ps != null) {
try {
ps.close();
// ps.close 에서도 예외처리를 해주는 이유
// SQLException이 발생할 수 있기 때문에 이를 잡아줘야 한다.
// 그렇지 않으면 Connection 을 close()하지 못하고 메소드를 빠져나갈 수 있다.
} catch (SQLException e) {
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
}
JDBC 조회 기능의 예외처리
조회를 위한 JDBC 코드는 좀 더 복잡해진다.
JDBC 예외처리를 적용한 getCount 메소드
public int getCount() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
c = this.dataSource.getConnection();
ps = c.prepareStatement("select count(*) from users");
rs = ps.executeQuery();
rs.next();
int count = rs.getInt(1);
return count;
} catch (SQLException e) {
throw e;
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
}
이제 UserDao의 모든 메소드에 동일한 방식으로 try/catch/finally 블록을 적용했다.
모든 작업을 마쳤으면 UserDaoTest 테스트를 수행해보고 이상이 없는지 확인한다.