JDBC 체크 예외 문제와 인터페이스
서비스 계층은 특정 기술에 종속되지 않고 순수성을 유지하는 것이 좋다. 하지만 JDBC를 사용할 때 SQLException이라는 체크 예외가 발생하여 인터페이스가 특정 기술에 종속되는 문제가 생긴다. 이를 해결하려면 체크 예외를 런타임 예외로 전환하여 처리하는 방법을 사용한다.
런타임 예외로 전환하는 이유
- 체크 예외(SQLException)를 사용하면 인터페이스에도 이 예외를 명시해야 해서 특정 기술(JDBC)에 종속된다.
- 런타임 예외를 사용하면 인터페이스에 별도 선언 없이 예외를 던질 수 있어 서비스 계층이 특정 기술에 종속되지 않게 된다.
직접 만든 런타임 예외 적용법
- MyDbException이라는 런타임 예외를 만들어 SQLException을 감싼다.
public class MyDbException extends RuntimeException {
public MyDbException(Throwable cause) {
super(cause);
}
}
- 데이터 접근 기술(JDBC)에서 발생하는 체크 예외(SQLException)를 런타임 예외(MyDbException)로 변환하여 서비스 계층으로 전달한다.
catch (SQLException e) {
throw new MyDbException(e);
}
이로 인해 서비스 계층은 JDBC라는 구체적인 구현 기술에 종속되지 않고 인터페이스만을 통해서 리포지토리를 사용할 수 있게 된다.
예외를 구분해 처리하는 방법
데이터베이스에서 특정 예외를 복구할 필요가 있을 때, 예외를 구분할 수 있도록 직접 예외를 만든다. 예를 들어 중복 키 예외(MyDuplicateKeyException)를 만들고, 이를 별도의 예외로 던져서 서비스 계층에서 복구 로직을 수행할 수 있다.
if (e.getErrorCode() == 23505) {
throw new MyDuplicateKeyException(e);
}
스프링의 데이터 접근 예외 추상화
스프링은 JDBC와 같은 특정 기술에 종속되지 않도록 데이터 접근 예외를 추상화하여 제공한다. 이를 통해 JDBC를 사용하든 JPA를 사용하든 서비스 계층은 스프링의 예외만을 사용하여 예외 처리를 할 수 있다.
- 스프링은 데이터베이스에서 나오는 다양한 오류 코드를 일관된 예외로 변환하는 SQLExceptionTranslator를 제공한다.
SQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException translatedException = exTranslator.translate("query", sql, e);
- 각각의 DB마다 SQL 에러코드가 다른데 스프링은 어떻게 각각의 DB가 제공하는 SQL 에러코드까지 고려해서 예외를 변환할 수 있을까?
- org.springframework.jdbc.support.sql-error-codes.xml 파일에서 에러코드와 예외를 매핑한다.
- 스프링 SQL 예외 변환기(SQLExceptionTranslator)는 이 파일에 대입해서 어떤 스프링 데이터 접근 예외로 전환해야 할지 찾아낸다
- 스프링의 데이터 접근 예외는 모두 런타임 예외이므로 인터페이스를 오염시키지 않고 순수성을 유지한다.
스프링의 JdbcTemplate 활용
스프링은 JDBC를 사용할 때 반복되는 다음과 같은 문제들을 해결하는 JdbcTemplate을 제공한다.
- 커넥션 획득 및 동기화
- PreparedStatement 처리
- SQL 쿼리 실행
- 예외 처리 및 변환
- 리소스 자동 관리
다음은 JdbcTemplate을 사용한 간단한 예시이다.
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = "insert into member(member_id, money) values(?, ?)";
template.update(sql, member.getMemberId(), member.getMoney());
JdbcTemplate의 추가 기능과 활용
NamedParameterJdbcTemplate
JdbcTemplate의 기본적인 순서 기반 파라미터 바인딩은 유지보수가 어렵다. 이를 해결하기 위해 스프링은 이름을 기준으로 파라미터를 바인딩하는 NamedParameterJdbcTemplate을 제공한다.
NamedParameterJdbcTemplate namedTemplate = new NamedParameterJdbcTemplate(dataSource);
String sql = "insert into item (item_name, price) values (:itemName, :price)";
SqlParameterSource params = new MapSqlParameterSource()
.addValue("itemName", "Item1")
.addValue("price", 10000);
namedTemplate.update(sql, params);
BeanPropertySqlParameterSource
객체의 프로퍼티를 자동으로 파라미터로 매핑하여 전달하는 데 사용된다.
SqlParameterSource params = new BeanPropertySqlParameterSource(item);
namedTemplate.update(sql, params);
SimpleJdbcInsert
SimpleJdbcInsert를 사용하면 간단한 Insert 작업을 더욱 쉽게 처리할 수 있다.
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(dataSource)
.withTableName("item")
.usingGeneratedKeyColumns("id");
SqlParameterSource params = new BeanPropertySqlParameterSource(item);
Number key = jdbcInsert.executeAndReturnKey(params);
item.setId(key.longValue());
BeanPropertyRowMapper
데이터베이스 조회 결과(ResultSet)를 자동으로 객체로 매핑한다. 컬럼명과 객체의 프로퍼티 이름이 다를 경우 별칭(alias)을 사용하면 된다.
private RowMapper<Item> itemRowMapper() {
return BeanPropertyRowMapper.newInstance(Item.class);
}
List<Item> items = namedTemplate.query("select id, item_name as itemName from item", itemRowMapper());
자바빈(JavaBean) 규약
자바빈(JavaBean) 규약이란 객체의 속성에 접근할 때 getter와 setter라는 메서드를 사용하는 규약을 말한다.
- 프로퍼티가 itemName이라면 메서드는 getItemName(), setItemName() 형태로 작성해야 한다.
- BeanPropertySqlParameterSource와 BeanPropertyRowMapper는 이 규약을 기반으로 객체와 데이터베이스의 결과를 자동으로 매핑한다.
정리
- 체크 예외를 런타임 예외로 변환하면 인터페이스가 특정 기술에 종속되지 않는다.
- 스프링이 제공하는 데이터 접근 예외 추상화와 예외 변환기 덕분에 서비스 계층은 데이터 접근 기술의 변경에도 영향을 받지 않고 유지할 수 있다.
- JdbcTemplate과 관련된 기능들은 JDBC의 반복 작업을 효과적으로 줄이고, 코드를 더 깔끔하고 유지보수하기 쉽게 만들어준다.
'Spring' 카테고리의 다른 글
[Spring] Spring Transaction 핵심 요약 (0) | 2025.04.10 |
---|---|
[Spring Core] Spring Core 핵심 개념 (0) | 2025.02.15 |
[Spring Core] 스프링을 사용하는 이유? (0) | 2025.02.15 |