2023. 1. 30. 12:49ㆍspring/db
1/30
기존 코드는 모두 체크 예외(SQLException)에 종속되어 있다.
-> 향후 새로운 기술로 변경할 때 코드를 전부 변경해줘야 한다.
문제 해결 방향
- 예외 누수 해결
- 반복 예외 던지기 제거
* 런타임 예외 이용하기
- RuntimeException을 상속 받은 우리만의 런타임예외 만들기
//RuntimeException 상속
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);
}
}
- SQLException이 터지면 MyDbException으로 묶어서 던져준다.
catch (SQLException e) {
throw new MyDbException(e);
그럼 밑의 throws SQLException 을 모두 제거할 수 있다.
public void delete(String memberId) throws SQLException
주의할 점은 throw new MyDbException(e) 와 같이 예외를 꼭 넣어서 던져야 한다.
다음은, 해당 레포지토리를 이용하는 서비스 계층에서도 반복된 예외처리를 제거해야 한다.
(우선 반복된 예외 처리가 없는 레포지토리를 인터페이스로 구현하고 상속 받아서 사용하자)
public interface MemberRepository {
Member save(Member member);
Member findById(String memberId);
void update(String memberId, int money);
void delete(String memberId);

-> 레포지토리를 인퍼페이스로 구현해 놓으면 서비스 계층 등에서 레포지토리를 사용할때 인터페이스를 주입받도록 설정해 놓으면 유지보수가 편해진다. (DI)
-> 레포지토리 기술을 변경할때도 편해진다.
서비스 계층에서 해당 인터페이스를 주입받도록 설정한다.
변경전
MemberRepositoryV3 memberRepository) {
this.memberRepository = memberRepository;
}
변경후
MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
========================================================================================
* 특정 예외 구분하기
- 데이터베이스에서는 예외에 해당 오류 코드를 포함해서 보내준다. (db마다 다름)
ex) e.getErrorCode() == 23505
- 해당 오류에 대한 예외 클래스를 새로 만든다.
public class MyDuplicateKeyException extends MyDbException {
-> 기술에 종속적이지 않아서 서비스계층의 순수성을 유지할 수 있다.
-> 향후 기술 변경시에도 예외는 그대로 유지해도 된다.
* 특정 예외 사용
if (e.getErrorCode() == 23505) {
throw new MyDuplicateKeyException(e);
}
- 다음과 같이 오류 코드에 따른 예외를 설정해서 던져준다. (예시는 중복 예외)
catch (MyDuplicateKeyException e) {
log.info("키 중복, 복구 시도");
String retryId = generateNewId(memberId);
log.info("retryId={}", retryId);
repository.save(new Member(retryId, 0));
} catch (MyDbException e) {
log.info("데이터 접근 계층 예외", e);
throw e;
- 예외처리는 각 예외에 따라 예외처리를 다르게 하여 특정 예외에 따른 대처가 가능하다.
* SQL 의 에러코드는 데이터베이스마다 다르다는 한계가 있다.
키 중복 오류를 예시로 들면
H2 는 23505 이고, MySQL 은 1062 이다.
==========================================================================================
* 스프링에서 제공하는 예외 처리
- 데이터 접근 계층에 대한 수많은 예외를 정리해서 제공한다.
- 기술에 종속되지 않기 때문에 Jdbc든 JPA든 사용할 수 있다.
최상위 계층으로 DataAccessException 을 상속받는다. (런타임오류)
* 스프링에서 제공하는 예외 변환기 (무슨 예외인지 확인하고 그에 맞는 예외를 반환해준다.)
void exceptionTranslator(){
String sql = "select bad grammer";
try {
Connection con = dataSource.getConnection();
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.executeQuery();
} catch (SQLException e) {
assertThat(e.getErrorCode()).isEqualTo(42122);
SQLErrorCodeSQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException resultEx = exTranslator.translate("select", sql, e);
log.info("resultEx", resultEx);
assertThat(resultEx.getClass()).isEqualTo(BadSqlGrammarException.class);
}
}
SQLErrorCodeSQLExceptionTranslator -> 예외 변환 객체
.translate(1. "읽을 수 있는 설명 (주로 해당 함수 네임)" 2. sql 문장, 3. 예외(e))
--> 파일에 각각 데이터베이스의 에러 코드가 정리되어 있다.
'spring > db' 카테고리의 다른 글
스프링 db2 - MyBatis (0) | 2023.02.01 |
---|---|
스프링 db2 - JdbcTemplate 사용 정보 (0) | 2023.01.31 |
스프링 db1 - JdbcTemplate (0) | 2023.01.30 |
스프링 DB1 - 스프링 트랜잭션 (0) | 2023.01.27 |
스프링 DB1 - JDBC 구조(DataSource, 커넥션풀, 트랜잭션 등) (0) | 2023.01.26 |