Database

스프링 DB - 예외 처리, 반복

taey 2024. 10. 6. 20:23

스프링 예외 추상화 이해

스프링 데이터 접근 예외 계층 

  • 스프링은 데이터 접근 게층에 대한 수십가지 예외를 정리해서 일관된 예외 계층을 제공한다.
  • 각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다.
  • JDBC나 JPA를 사용할 때 발생하는 예외를 스프링이 제공한느 예외로 변환해주는 역할도 스프링이 제공한다.
  • 그림 단순화를 위해 일부 계층을 생략했다.

 

  • 예외의 최고 상위는 org.springframework.dao.DataAccessException 이다. 런타임 예외를 상속 받았기 때문에 스프링이 제공하는 데이터 접근 계층의 모든 예외는 런타임 예외이다. 
  • DataAccessException은 크게 2가지로 구분하는데 NonTransient 예외와 Transient 예외이다.
    • Transient는 일시적이라는 뜻이다. Transient 하위 예외는 동일한 SQL을 다시 시도했을 때 성공할 가능성이 있다.
      • 예를 들어서, 쿼리 타임아웃, 락과 관련된 오류들이다. 이런 오류들은 데이터베이스 상태가 좋아지거나, 락이 풀렸을 때, 다시 시도하면 성공할 수도 있다.   
    • NonTrasient는 일시적이지 않다는 뜻이다. 같은 SQL을 그대로 반복해서 실행하면 실패한다.
      • 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