<토비의스프링> 5.1~5.2 서비스추상화
by BFine5.1 사용자레벨 관리 기능 추가
레벨 관리 기능 추가
- 사용자 레벨을 BASIC, SILVER, GOLD 설정
- DB로 처리하는 방법
- 비효율적
- 상수형태로 처리하는 방법
- 의미없는 숫자를 프로퍼티에 사용할 경우 타입 문제발생
- Enum 활용
- 타입 안전성 (DB에 insert할 경우에는 지정한타입으로 변경 )
- DB로 처리하는 방법
- 사용자 레벨을 BASIC, SILVER, GOLD 설정
사용자 수정 기능 추가
기본키인 id를 제외한 나머지필드는 수정될 가능성이 있다.
수정 기능 테스트 추가
원하는 사용자 외의 정보는 변경 되지 않았음을 직접확인
where을 잊어버릴경우 문제가 될 수 있음
@Test public void update() { dao.deleteAll(); dao.add(user1); dao.add(user2); user1.setName("오민규"); user1.setPassword("springno6"); user1.setLevel(Level.GOLD); user1.setLogin(1000); user1.setRecommend(999); dao.update(user1); User user1update = dao.get(user1.getId()); checkSameUser(user1, user1update); User user2same = dao.get(user2.getId()); checkSameUser(user2, user2same); }
- 코드 개선
- 코드에 중복된 건 없는지
- 이해하기 불편하지 않은지
- 자신이 있어야할 자리에 있는지
- 변화에 쉽게 대응할 수 있는지
if(user.getLevel() == Level.BASIC) && user.getLogin() >= 50{
// 새로운 레벨이 추가될 경우 enum 수정 및 계속 늘어남
user.setLevel(level.SILVER);
changed = true; // 아래를 위한 플래그
}
...
if(changed){...}
- 레벨 분기
public static Level valueOf(int value) {
switch(value) {
case 1: return BASIC;
case 2: return SILVER;
case 3: return GOLD;
default: throw new AssertionError("Unknown value: " + value);
}
}
- enum에 레벨 값을 담도록 수정
public enum Level {
GOLD(3, null), SILVER(2, GOLD), BASIC(1, SILVER);
5.2 트랜잭션 서비스 추상화
서버에 문제가 생긴다면 사용자 레벨을 어떻게 할 것 인가?! → 모든 변경작업 취소
테스트 시나리오
- 5명의 사용자 정보를 DB에 추가
- 수행하다가 중간에 예외 발생
모든 사용자 레벨을 업그레이드 하는 작업을 하나의 트랜잭션 안에 두어야 한다.
- 트랜잭션은 더이상 나눌수없는 작업의 단위를 의미한다.
- 전체가 성공하거나 전체가 실패해야한다.
트랜잭션 경계설정
트랜잭션이 시작되고 끝나는 위치를 트랜잭션 경계라고 부른다.
Connection c = dataSource.getConnection(); c.setAutoCommit(false); // 하나의 트랜잭션 try { PreparedStatement st1 = c.prepareStatement("..."); st1.executeUpdate(); PreparedStatement st2 = c.prepareStatement("..."); st2.executeUpdate(); c.commit(); // 성공 } catch (Exception e) { c.rollback(); // 실패 }
하나의 커넥션이 만들어지고 닫히는 범위 안에 존재
하나의 DB커넥션안에서 만들어지는 트랜잭션을 로컬트랜잭션이라고 한다.
UserService와 UserDao의 트랜잭션 문제
트랜잭션 경계설정 코드가 존재하지 않음
- upgrade 메서드를 개별적으로 호출 → 각각의 트랜잭션을 가짐
해결방법
upgradeLevels 메소드의 내용을 옮기기(X)
- 비지니스로직과 데이터로직을 한데 묶는 결과
- UserService와 UserDao를 그대로 둔체로 트랜잭션 적용해야함
트랜잭션 경계를 upgradeLevels 두기(+ DB 커넥션 )
class UserService { public void upgradeLevels() throws Exception { Connection c = ...; try { ... upgradeLevel(c, user); ... } } protected void upgradeLevel(Connection c, User user) { ... }
- 문제점
- JdbcTemplate을 더이상 활용 못함
- UserService에 Connection 파라미터 추가돼야함
- UserDao는 독립적이지 않게 된다.
트랜잭션 동기화
UserService에서 트랜잭션을 시작하기 위해 만든 Connection 오브젝트를 저장소에 보관해두고 이후 호출되는 Dao의 메소드에서는 저장된 Connection을 가져다가 사용하게 하는 것
public void upgradeLevels() throws Exception { // 트랜잭션 동기화 관리자를 이용해 동기화 작업을 초기화 한다. TransactionSynchronizationManager.initSynchronization(); Connection c = DataSourceUtils.getConnection(dataSource); c.setAuthCommit(false); try { List<User> users = UserDao.getAll(); for (User user : users) { if (canUpdatedLevel(user)) { upgradeLevel(user); } } c.commit(); } catch (Exception e) { c.rollback(); throw e; } finally { DataSourceUtils.releaseConnection(c, dataSource); TransactionSynchronizationManager.unbindResource(this.dataSource); TransactionSynchronizationManager.clearSynchronization(); } }
스프링이 제공하는 DataSourceUtils 사용
- 커넥션 오브젝트 생성 뿐 아니라 트랜잭션 동기화 사용하도록 저장소에 바인딩해줌
JdbcTemplate은 동기화를 확인하여 저장소에 있는 DB 커넥션을 사용한다.
트랜잭션 서비스 추상화
한개 이상의 DB로의 작업을 하나의 트랜잭션으로 만드는 건 로컬트랜잭션으로 불가능
- 로컬트랜잭션은 하나의 DB 커넥션에 종속
여러 DB를 사용할 경우 글로벌 트랜잭션을 적용해야한다. → Java의 JTA API
InitialContext ctx = new InitialContext(); UserTransaction tx = (UserTransaction) ctx.lookup(USER_TX_JNDI_NAME);// JNDI tx.begin(); Connection c = dataSource.getConnection(); try { tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } finally { c.close(); }
UserService의 코드를 수정해야하는 문제점
트랜잭션 API의 의존관계 문제와 해결책
스프링의 트랜잭션 서비스 추상화
class UserService { private PlatformTransactionManager transactionManager; = new DataSourceTransactionManager(dataSource); TransactionStatus ts = transactionManager.getTransaction (new DefaultTransactionDefintion()); try{ ... // 작업 transactionManager.commit(ts); }catch{ transactionManager.rollback(ts); }
DataSourceTransactionManager 는 JdbcTemplate 에서 사용될수 있게 트랜잭션 관리해줌
'개발서적 > 토비의스프링' 카테고리의 다른 글
<토비의스프링> 6.4~6.5 AOP(2) (0) | 2021.02.20 |
---|---|
<토비의스프링> 6.1~6.3 AOP (0) | 2021.02.18 |
<토비의스프링> 4.1~4.2 예외 (0) | 2021.02.17 |
<토비의스프링> 3.1~3.5 템플릿 (0) | 2021.02.16 |
<토비의스프링> 1.7~1.8 DI (0) | 2021.02.15 |
블로그의 정보
57개월 BackEnd
BFine