Conversation
| fail("TetUserSericeException expected"); | ||
| } catch (TestUserServiceException e) { | ||
| } | ||
| checkLevelUpgraded(users.get(1), false); |
There was a problem hiding this comment.
p353. 테스트는 실패한다.
모든 사용자의 레벨을 업그레이드하는 작업인 upgradeLevels() 메소드가 하나의 트랜잭션 안에서 동작하지 않았기 때문이다.
트랜잭션이란 더 이상 나눌 수 없는 단위 작업을 말한다. (트랜잭션의 핵심 속성인 원자성)
|
p. 353
작업별 트랜잭션 정의 👉🏻 더이상 쪼개질 수 없는지! |
데이터 액세스 코드를 DAO로 만들어서 분리해놓았을 경우에는 이처럼 DAO 메서드를 호출할 때마다 하나의 새로운 트랜잭션이 만들어지는 구조가 될 수 밖에 없다. (jdbc api를 직접 사용하든, JdbcTemplate을 이용하든) // JdbcTemplate에서 sql을 실행할 때 사용하는 메서드
private <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action, boolean closeResources) throws DataAccessException {
// ...
Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
PreparedStatement ps = null;
// ...
} |
|
그럼? UserService 트랜잭션 경계설정의 문제점
|
+) 이 과정에서 예외가 발생하는 때에는 Connection.rollback()을 호출 후 동기화저장소에서 Connection 객체 제거. 트랜잭션 동기화 저장소는 작업 스레드마다 독립적으로 Connection 객체를 저장하고 관리하기 때문에 멀티스레드 환경에서도 충돌이 날 염려는 없다. |
|
이렇게 우리가 따로 JdbcTemplate은 수정할 필요도, 다른 요청을 할 필요도 없으므로 비즈니스 로직 레벨의 트랜잭션을 적용했지만 |
PlatformTransactionManager 를 이용하여 데이터액세스계층에 대한 종속성을 제거했다. datasource를 통해 각기 다른 transaction manager를 동작시킬 수 있다.
kyY00n
left a comment
There was a problem hiding this comment.
스프링의 트랜잭션 서비스 추상화 (PlatformTransactionManager)
| protected void upgradeLevel(User user) { | ||
| if (user.getId().equals(this.id)) { | ||
| throw new TestUserServiceException(); | ||
| } | ||
| super.upgradeLevel(user); | ||
| } |
There was a problem hiding this comment.
UserService에서 protected 접근자로 수정한 덕분에 upgradeLevel을 원하는대로 변경할 수 있게 되었다.
| c.setAutoCommit(false); | ||
| public void upgradeLevels() { | ||
| PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); | ||
| TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); |
There was a problem hiding this comment.
transaction을 시작한다는 의미라고 생각하자.
There was a problem hiding this comment.
트랜잭션이 TransactionStatus 타입의 변수에 저장된다.
| upgradeLevel(user); | ||
| } | ||
| c.commit(); | ||
| transactionManager.commit(status); |
There was a problem hiding this comment.
트랜잭션에 대한 조작이 필요할 때 PlatformTransactionManager의 메서드에 트랜잭션을 인자로 전달해주면 된당.
| } finally { | ||
| DataSourceUtils.releaseConnection(c, dataSource); | ||
| TransactionSynchronizationManager.unbindResource(this.dataSource); | ||
| TransactionSynchronizationManager.clearSynchronization(); |
There was a problem hiding this comment.
질문. 이런 건 transactionManager가 따로 안해줘도 되는건가
There was a problem hiding this comment.
답변. 응 . commit() 이랑 rollback()에 relase 로직이 포함돼있어
PlatformTransactionManager 변수에 구현체가 바뀔때마다 코드가 변경됐었다. 구현 클래스의 변경에 열려있지만 변경되지 않는 코드를 작성하기 위해 UserService에 DI 해서 사용한다.
kyY00n
left a comment
There was a problem hiding this comment.
이제 UserService는 트랜잭션 기술에서 완전히 독립적인 코드가 됐다.
DAO가 하이버네이트, JPA, JDO 등을 사용하도록 수정한다해도,
UserService의 코드는 수정할 필요가 없다.
| <bean id="transactionManager" | ||
| class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
There was a problem hiding this comment.
구현체를 바꾸고 싶으면 여기에서 클래스를 바꿔주면 된다.
ex) JTA 를 사용하고 싶은 경우
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- ...빈 주입 -->
</bean>이런식으로 바꾸면 된당.
|
수평적인 구분- |
dao의 add()와 update() 도 함께 수정

No description provided.