6.8.1 선언적 트랜잭션과 트랜잭션 전파 속성
- REQUIRED로 전파 속성을 설정하면 앞에서 트랜잭션이 진행되고 있으면 새로운 트랜잭션을 시작하지 않고, 기존의 트랜잭션에 참여를 함
- REQUIRED 전파 속성을 가진 메소드를 결합해 다양한 크기의 트랜잭션 작업을 만들 수 있음
- A 메소드에서 B 메소드를 호출하는데, A와 B 작업이 모두 완료되어야만할 때, REQUIRED 전파 속성을 사용해야 함
- AOP를 이용해 코드 외부에서 트랜잭션 기능을 부여해주는 방법을 선언적 트랜잭션(Declarative Transaction)이라 함
- 대조적으로, TransactionTemplate나 개별 데이터 트랜잭션 API를 사용해 직접 코드 안에서 사용하는 방법을 프로그램에 의한 트랜잭션(Programmatic Transaction)이라 함
- 특수한 상황을 제외하고, 선언적 트랜잭션 방법을 사용하는 것을 추천함
6.8.2 트랜잭션 매니저와 트랜잭션 동기화
- AOP 덕분에 트랜잭션 부가기능을 간단히 애플리케이션 전반에 적용할 수 있음
- AOP의 중요한 기술적 기반은 트랜잭션 추상화임
- 트랜잭션 추상화의 핵심은 트랜잭션 매니저와 트랜잭션 동기화임
- PlatformTransactionManager 인터페이스를 구현한 트랜잭션 매니저를 통해 구체적인 트랜잭션 기술의 종류에 상관없이 일관된 트랜잭션 제어가 가능
- TransactionSynchronization을 사용하는 트랜잭션 동기화는 같은 DB Connection을 공유하게 함
- 즉, 트랜잭션 동기화 기술은 DB Connection을 공유하는 특성을 이용하여 트랜잭션 전파 속성에 따라 참여할 수 있도록 함
6.8.3 트랜잭션 매니저를 이용한 트랜잭션용 트랜잭션 제어
- 테스트와 특별한 이유가 있다면 트랜잭션 매니저를 통해 트랜잭션을 제어할 수 있음
- 간단한 테스트 메소드
public class UserServiceTest {
...
@Test
public void transactionSync(){
userService.deleteAll();
userService.add(users.get(0));
userService.add(users.get(1));
}
}
- 간단한 테스트 메소드에서 실행되는 트랜잭션은 3개임
- 각 메소드마다 트랜잭션이 독립적으로 작용함
- 트랜잭션 매니저를 사용하여 세 개의 메소드를 하나로 묶을 수 있음
- 메소드가 실행하기 전에 트랜잭션 매니저에서 강제로 트랜잭션을 시작하면 됨
public class UserServiceTest {
...
@Test
public void transactionSync(){
//기본 값 사용
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
userService.deleteAll();
userService.add(users.get(0));
userService.add(users.get(1));
transactionManager.commit(txStatus);
}
}
6.8.4 트랜잭션 동기화 검증
- 트랜잭션 속성을 변경해서 검증할 것임
public class UserServiceTest {
...
@Test
public void transactionSync(){
//기본 값 사용
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
txDefinition.setReadOnly(true);
...
}
}
- 트랜잭션 이라면 롤백도 가능해야 함
public class UserServiceTest {
...
@Test
public void transactionSync(){
userDao.deleteAll();
assertThat(userDao.getCount(), is(0));
//기본 값 사용
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
userService.add(users.get(0));
userService.add(users.get(1));
assertThat(userDao.getCount(), is(2));
transactionManager.rollback(txStatus);
assertThat(userDao.getCount(), is(0));
}
}
6.8.5 롤백 테스트
- 롤백 테스트란, 테스트가 끝나면 무조건 롤백을 하는 테스트를 말함
- 복잡한 데이터를 바탕으로 동작하는 통합 테스트에서 DB의 상태가 중요할 수 있음
- 테스트 수행으로 데이터 추가, 삭제 등이 발생 하면 다른 통합 테스트에 영향을 줄 수 있음
- 때문에 테스트 수행 시 마지막에 롤백을 해서 테스트 실행 전 상태로 되돌림
- 적절한 격리 수준만 보장하면 동시에 여러 개의 통합 테스트를 진행할 수 있음
public class UserServiceTest {
...
@Test
public void transactionSync() {
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
try {
userService.deleteAll();
userService.add(users.get(0));
userService.add(users.get(1));
} finally {
transactionManager.rollback(txStatus);
}
}
}
6.8.6 테스트를 위한 트랜잭션 애노테이션
- @Transactional
- @Rollback
- @TransactionConfiguration
- @NotTransactional과 Propagation.NEVER
6.8.7 @Transactional
- @Transactional을 사용하면 트랜잭션 경계설정이 자동 설정됨
- 디폴트는 REQUIRED임
6.8.8 @Rollback
- @Transactional을 사용하는 애플리케이션 클래스와 테스트 클래스의 디폴트 속성은 동일함
- 단, 테스트 클래스는 테스트 수행 후 자동으로 롤백됨
- 테스트 수행 후 롤백을 원하지 않을 때 @Rollback 사용
- @Rollback의 기본값은 true이기 때문에 false로 선언해야 롤백되지 않음
public class UserServiceTest {
...
@Test
@Transactional
@Rollback(false)
public void transactionSync() {
...
}
}
6.8.9 @TransactionConfiguration
- @Rollback은 메소드 레벨에서만 사용 가능함
- 클래스 레벨에서 적용하기 위해 @TransactionConfiguration을 사용해야 함
- @Rollback과 동일하게 false로 선언해야 롤백되지 않음
- @TransactionConfiguration을 사용한 클래스에서, 특정 메소드는 롤백하고 싶을 경우 해당 메소드에만 @Rollback 사용하면 됨
- 클래스보다 메소드가 우선순위가 더 높기 때문임
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
@Transactional
@TransactionConfiguration(defaultRollback = false) //롤백되지 않음
public class UserServiceTest {
@Test
@Rollback //롤백됨
public void add(){}
}
6.8.10 @NotTransactional과 Propagation.NEVER
- @Transactional이 있는 클래스에서 특정 메소드를 트랜잭션 동작하고 싶지 않을 때 사용
- @NotTransactional는 deprecated 되었음
- 대신, Propagation.NEVER사용
@Transactional(propagation = Propagation.NEVER)
6.8.11 효과적인 DB 테스트
- 테스트 내에서 트랜잭션을 제어할 수 있는 네 가지 애노테이션을 잘 활용하면 통합 테스트를 만들 때 편리함
- 단위 테스트와 통합 테스트는 클래스로 구분하는게 좋음
- DB가 사용되는 통합 테스트는 롤백 테스트로 만드는 것이 좋음
'토비의 스프링 정리' 카테고리의 다른 글
토비의 스프링 - 7.1 SQL과 DAO의 분리 (0) | 2022.10.25 |
---|---|
토비의 스프링 - 6.9 6장 정리 (0) | 2022.10.20 |
토비의 스프링 - 6.7 애노테이션 트랜잭션 속성과 포인트컷 (0) | 2022.10.20 |
토비의 스프링 - 6.6 트랜잭션 속성 (0) | 2022.10.20 |
토비의 스프링 - 6.5 스프링의 AOP (0) | 2022.10.20 |