728x90
스프링이 제공하는 데이터 접근 예외 추상화와 SQL 예외 변환기를 적용해보자
/**
* SQLExceptionTranslator 추가
*/
@Slf4j
public class MemberRepositoryV4_2 implements MemberRepository {
private final DataSource dataSource;
private final SQLExceptionTranslator exTranslator;
public MemberRepositoryV4_2(DataSource dataSource) {
this.dataSource = dataSource;
this.exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource); //DB 정보를 알기위해 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) {
throw exTranslator.translate("save", sql, e);
} finally {
close(con, pstmt, null);
}
}
public Member findById(String memberId) {
String sql = "select * from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
rs = pstmt.executeQuery();
if (rs.next()) { //최소 한번은 호출해야 데이터가 있는지 없는지 알수 있다
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId = " + memberId);
}
} catch (SQLException e) {
throw exTranslator.translate("findById", sql, e);
} finally {
close(con, pstmt, rs);
}
}
public void update(String memberId, int money) {
String sql = "update member set money = ? where member_id=?";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, memberId);
int resultSize = pstmt.executeUpdate();//쿼리 실행
log.info("resultSize = {}", resultSize);
} catch (SQLException e) {
throw exTranslator.translate("update", sql, e);
} finally {
close(con, pstmt, null);
}
}
public void delete(String memberId) {
String sql = "delete from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
pstmt.executeUpdate();
} catch (SQLException e) {
throw exTranslator.translate("delete", sql, e);
} finally {
close(con, pstmt, null);
}
}
private Connection getConnection() throws SQLException {
//주의 트랜잭션 동기화를 사용하려면 DataSourceUtils 를 사용한다.
Connection con = DataSourceUtils.getConnection(dataSource);
log.info("get Connection={}, class", con, con.getClass());
return con;
}
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
//주의! 트랜잭션 동기화를 사용하려면 DataSourceUtils를 사용해야 한다.
DataSourceUtils.releaseConnection(con, dataSource);
}
}
- 기존 코드에서 스프링 예외 변환기를 사용하도록 변경되었다.
MemberServiceV4Test
@TestConfiguration
static class TestConfig {
private final DataSource dataSource;
public TestConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberRepository memberRepository() {
return new MemberRepositoryV4_2(dataSource);
}
@Bean
public MemberServiceV4 memberServiceV4() {
return new MemberServiceV4(memberRepository());
}
}
- Repository Bean만 변경하여 생성하면 된다.
정리
드디어 예외에 대한 부분을 깔끔하게 정리했다.
스프링이 예외를 추상화해준 덕분에, 서비스 계층은 특정 리포지토리의 구현 기술과 예외에 종속적이지 않게 되었다. 따라서 서비스 계층은 특정 구현 기술이 변경되어도 그대로 유지할 수 있게 되었다. 다시 DI를 제대로 활용할 수 있게 된 것이다.
추가로 서비스 계층에서 예외를 잡아서 복구해야 하는 경우, 예외가 스프링이 제공하는 데이터 접근 예외로 변경되어서 서비스 계층에 넘어오기 때문에 필요한 경우 예외를 잡아서 복구하면 된다
728x90
'스프링 DB 1편(데이터 접근 핵심 원리)' 카테고리의 다른 글
| Ch06. 스프링과 문제 해결(예외 처리, 반복) - JDBC 반복 문제 해결( JdbcTemplate) (0) | 2022.05.01 |
|---|---|
| Ch06. 스프링과 문제 해결(예외 처리, 반복) - 스프링 예외 추상화 이해 (0) | 2022.05.01 |
| Ch06. 스프링과 문제 해결(예외 처리, 반복) - 데이터 접근 예외 직접 만들기 (0) | 2022.05.01 |
| Ch06. 스프링과 문제 해결(예외 처리, 반복) - 런타임 예외 적용 (0) | 2022.05.01 |
| Ch06. 스프링과 문제 해결(예외 처리, 반복) - 체크 예외와 인터페이스 (0) | 2022.05.01 |