예외는 잡아서 처리하거나 던져야한다.
예외를 잡거나 던질ㄷ 때 지정한 예외뿐만 아니라 하위 예외까지 함께 처리된다.
체크예외
체크예외는 예외를 잡아서 처리할 수 없을 때, 예외를 밖으로 던지는 'throw'를 반드시 선언해야한다. 그렇지 않으면 컴파일 오류가 발생
언체크예외 (런타임예외)
예외를 잡아서 처리하지 않아도 'throw'를 생략할 수 있다.
언체크예외를 사용해야하는 이유
기본적으로는 언체크예외(런타임예외)를 사용한다.
체크예외는 로직상 의도적으로 던지는 예외에만 사용
체크예외 -> 언체크예외(런타임예외) 전환
static class Repository {
public void call() {
try {
runSQL();
} catch (SQLException e) {
//기존예외 e 포함
throw new RuntimeSQLException(e);
}
}
//체크예외 SQLException
private void runSQL() throws SQLException {
throw new SQLException("ex");
}
}
//언체크예외(런타임예외) SQLException
static calss RuntimeSQLException extends RuntimeExcepiton {
public RuntimeSQLException(){
}
//기존예외 포함 생성자
public RuntimeSQLException(Throwable cause){
super(cause);
}
}
MyDbException 런타임 예외
public class MyDbException extends RuntimeException{
public MyDbException() {
}
public MyDbException(String message) {
super(message);
}
public MyDbException(String message, Throwable cause) {
super(message, cause);
}
public MyDbException(Throwable cause) {
super(cause);
}
}
리포지토리
* 예외 누수 문제 해결
* 체크예외를 런타임 예외로 변경
* MemberRepository 인터페이스 사용
* throws SQLException 제거
public class MemberRepositoryV4_1 implements MemberRepository{
private final DataSource dataSource;
public MemberRepositoryV4_1(DataSource dataSource) {
this.dataSource = dataSource;
}
public Member save(Member member) {
String sql = "insert into member(member_id,money) values (?, ?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate();
return member;
} catch (SQLException e) {
//SQLException을 MyDbException이라는 런타임예외로 변환
throw new MyDbException(e);
} finally {
close(con,pstmt, null);
}
}
}
스프링은 데이터 접근과 관련된 예외를 추상화해서 제공한다.
해당 추상화를 사용하면 JDBC나 JPA 어떤 기술을 사용하든 스프링이 제공하는 예외를 사용하면 된다.
JDBC나 JPA를 사용시에 발생하는 예외를 스프링이 제공하는 예외로 변환해주는 역할도 제공
SQLExceptionTranslator 추가
public class MemberRepositoryV4_2 implements MemberRepository{
private final DataSource dataSource;
//스프링이 제공하는 SQL예외 변환기
private final SQLExceptionTranslator exTranslator;
public MemberRepositoryV4_2(DataSource dataSource) {
this.dataSource = dataSource;
this.exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
}
@Override
public Member save(Member member) {
String sql = "insert into member(member_id,money) values (?, ?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate();
return member;
} catch (SQLException e) {
//"save" 해당 예외에 대한 설명
//sql 실행한 sql
//e 마지막에 발생된 SQLException
throw exTranslator.translate("save", sql, e);
} finally {
close(con,pstmt, null);
}
}
}
스프링은 JDBCD의 반복 문제를 해결하기 위해 템플릿을 제공
JDBCTemplate 사용
리포지토리
public class MemberRepositoryV5 implements MemberRepository{
//JDBC Template 사용
private final JdbcTemplate template;
public MemberRepositoryV5(DataSource dataSource) {
this.template = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
String sql = "insert into member(member_id,money) values (?, ?)";
//
template.update(sql, member.getMemberId(), member.getMoney());
return member;
}
}