스프링 예외 추상화 이해
스프링 데이터 접근 예외 계층

- 스프링은 데이터 접근 게층에 대한 수십가지 예외를 정리해서 일관된 예외 계층을 제공한다.
- 각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다.
- JDBC나 JPA를 사용할 때 발생하는 예외를 스프링이 제공한느 예외로 변환해주는 역할도 스프링이 제공한다.
- 그림 단순화를 위해 일부 계층을 생략했다.
- 예외의 최고 상위는 org.springframework.dao.DataAccessException 이다. 런타임 예외를 상속 받았기 때문에 스프링이 제공하는 데이터 접근 계층의 모든 예외는 런타임 예외이다.
- DataAccessException은 크게 2가지로 구분하는데 NonTransient 예외와 Transient 예외이다.
- Transient는 일시적이라는 뜻이다. Transient 하위 예외는 동일한 SQL을 다시 시도했을 때 성공할 가능성이 있다.
- 예를 들어서, 쿼리 타임아웃, 락과 관련된 오류들이다. 이런 오류들은 데이터베이스 상태가 좋아지거나, 락이 풀렸을 때, 다시 시도하면 성공할 수도 있다.
- NonTrasient는 일시적이지 않다는 뜻이다. 같은 SQL을 그대로 반복해서 실행하면 실패한다.
- SQL 문법 오류, 데이터베이스 제약 조건 위배 등이 있다.
- Transient는 일시적이라는 뜻이다. Transient 하위 예외는 동일한 SQL을 다시 시도했을 때 성공할 가능성이 있다.
스프링이 제공하는 예외 변환기
스프링은 데이터베이스에서 발생하는 오류 코드를 스프링이 정의한 예외로 자동으로 변환해주는 변환기를 제공한다.
SpringExceptionTranslatorTest
@Test
void exceptionTranslator() {
String sql = "select bad grammar";
try {
Connection con = dataSource.getConnection();
PreparedStatement stmt = con.prepareStatement(sql);
stmt.executeQuery();
} catch (SQLException e) {
assertThat(e.getErrorCode()).isEqualTo(42122);
//org.springframework.jdbc.support.sql-error-codes.xml
SQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
//org.springframework.jdbc.BadSqlGrammarException
DataAccessException resultEx = exTranslator.translate("select", sql, e);
log.info("resultEx", resultEx);
assertThat(resultEx.getClass()).isEqualTo(BadSqlGrammarException.class);
}
}
SQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException resultEx = exTranslator.translate("select", sql, e);
- translate() 메서드의 첫 번째 파라미터는 읽을 수 있는 설명이고, 두 번째는 실행한 sql, 마지막은 발생된 SQLException을 전달하면 된다. 이렇게 하면 적절한 스프링 데이터 접근 계층의 예외로 변환해서 반환해준다.
- 예제에서는 SQL 문법이 잘못되었으므로 BadSqlGrammarException 을 반환하는 것을 확인할 수 있다
- 눈에 보이는 반환 타입은 최상위 타입인 DataAccessException이지만 실제로는 BadSqlGrammarException 예외가 반환된다. 마지막에 assertThat() 부분을 확인하자.
- 참고로 BadSqlGrammarException 은 최상위 타입인 DataAccessException 를 상속 받아서 만들어진다.
각각의 DB마다 SQL ErrorCode는 다르다. 스프링은 어떻게 각각의 DB가 제공하는 ErrorCode까지 고려해서 예외를 반환할까?
sql-error-codes.xml
<bean id="H2" class="org.springframework.jdbc.support.SQLErrorCodes">
<property name="badSqlGrammarCodes">
<value>42000,42001,42101,42102,42111,42112,42121,42122,42132</value>
</property>
<property name="duplicateKeyCodes">
<value>23001,23505</value>
</property>
</bean>
<bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes">
<property name="badSqlGrammarCodes">
<value>1054,1064,1146</value>
</property>
<property name="duplicateKeyCodes">
<value>1062</value>
</property>
</bean>
- org.springframework.jdbc.support.sql-error-codes.xml
- 스프링 SQL 예외 변환기는 SQL ErrorCode를 이 파일에 대입해서 어떤 스프링 데이터 접근 예외로 전환해야 할지 찾아낸다.
JDBC 반복 문제 해결 - JdbcTemplate
JDBC 반복 문제
- 커넥션 조회, 커넥션 동기화
- PreparedStatement 생성 및 파라미터 바인딩
- 쿼리 실행
- 결과 바인딩
- 예외 발생 시 스프링 예외 변환기 실행
- 리소스 종료
스프링은 JDBC의 반복 문제를 해결하기 위해 JdbcTemplate이라는 템플릿을 제공한다.
public MemberRepositoryV5(DataSource dataSource) {
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;
}
'Database' 카테고리의 다른 글
| 스프링 DB - 트랜잭션 문제 해결 (2) | 2024.10.06 |
|---|---|
| 스프링 DB - 락 (4) | 2024.10.06 |
| 스프링 DB - 트랜잭션 (3) | 2024.10.05 |
| 스프링 DB - 커넥션풀과 데이터 소스 이해 (2) | 2024.10.05 |
| 스프링 DB - JDBC (1) | 2024.10.05 |