스프링/6. 스프링 DB-2

14. 데이터 롤백

sdafdq 2023. 10. 9. 01:08

테스트용 DB에서 각 테스트를 끝마치고 난 뒤 원래 상태로 되돌릴 좋은 방법은 바로 롤백이다.

오토커밋 모드를 꺼두면 커밋을 하기 전 까지 내 세션에만 반영이 되어 있기 때문에, 궁극적으로는 DB에 반영이 되지 않는 것이다.

그 상태로 테스트를 하다가, 테스트가 끝난 후, Rollback 시켜버리면 된다.

 

@BeforeEach에 오토커밋 모드를 끈 후,

테스트를 마치고 나면

@AfterEach에서 롤백을 하고 오토커밋 모드를 켜서 원래 상태로 되돌려 놓는다.

 

 

 

@SpringBootTest
class ItemRepositoryTest {

    @Autowired
    ItemRepository itemRepository;

    @Autowired
    PlatformTransactionManager transactionManager;

    TransactionStatus status;

    @BeforeEach
    void beforeEach(){
        status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    }

    @AfterEach
    void afterEach() {
        //MemoryItemRepository 의 경우 제한적으로 사용
        if (itemRepository instanceof MemoryItemRepository) {
            ((MemoryItemRepository) itemRepository).clearStore();
        }

        transactionManager.rollback(status);
    }

    @Test
    void save() {
        //given
        Item item = new Item("itemA", 10000, 10);

        //when
        Item savedItem = itemRepository.save(item);

        //then
        Item findItem = itemRepository.findById(item.getId()).get();
        assertThat(findItem).isEqualTo(savedItem);
    }

    @Test
    void updateItem() {
        //given
        Item item = new Item("item1", 10000, 10);
        Item savedItem = itemRepository.save(item);
        Long itemId = savedItem.getId();

        //when
        ItemUpdateDto updateParam = new ItemUpdateDto("item2", 20000, 30);
        itemRepository.update(itemId, updateParam);

        //then
        Item findItem = itemRepository.findById(itemId).get();
        assertThat(findItem.getItemName()).isEqualTo(updateParam.getItemName());
        assertThat(findItem.getPrice()).isEqualTo(updateParam.getPrice());
        assertThat(findItem.getQuantity()).isEqualTo(updateParam.getQuantity());
    }

    @Test
    void findItems() {
        //given
        Item item1 = new Item("itemA-1", 10000, 10);
        Item item2 = new Item("itemA-2", 20000, 20);
        Item item3 = new Item("itemB-1", 30000, 30);

        itemRepository.save(item1);
        itemRepository.save(item2);
        itemRepository.save(item3);

        //둘 다 없음 검증
        test(null, null, item1, item2, item3);
        test("", null, item1, item2, item3);

        //itemName 검증
        test("itemA", null, item1, item2);
        test("temA", null, item1, item2);
        test("itemB", null, item3);

        //maxPrice 검증
        test(null, 10000, item1);

        //둘 다 있음 검증
        test("itemA", 10000, item1);
    }

    void test(String itemName, Integer maxPrice, Item... items) {
        List<Item> result = itemRepository.findAll(new ItemSearchCond(itemName, maxPrice));
        assertThat(result).containsExactly(items);
    }
}

우리가 중요하게 볼 것은 @BeforeEach, @AfterEach, 그에 따라 추가된 것들.

먼저 transactionManager는 주입으로 받는다.

저 PlatformTransactionManager는 스프링이 서버를 시작할 때 자동으로 등록해 주는 Bean중 하나이다.

이름에 Platform이 붙은 이유는 이미 TransactionManager를 누가 사용하고 있었다고..

 

여튼, transactionManager.getTransaction() 하면 트랜잭션 시작이다.

저기 안에 new DefaultTransactionDefinition()을 인자로 넣는데,

직역하면 기본 트랜잭션 설정인데, 정말 말 그대로 기본 트랜잭션 설정이다. 트랜잭션 시작 전 오토커밋 모드 꺼 두고, 트랜잭션 끝나면 오토커밋 모드 다시 킨 상태로 반환시키고.. 뭐 그런 것일 것이다.

 

여튼 @BeforeEach는 한 @Test가 시작하기 전에 실행되는 거고, @AfterEach는 한 @Test가 완료된 후 실행되는 거니,

@BeforeEach에 transaction.getTransaction() 해서 트랜잭션을 시작하고,

@Test가 다 끝난 후 실행하는

@AfterEach에 

transactionManager.rollback() 해버려 원래 상태로 되돌려 버린다.

 

 

커넥션을 가져올 때, 만약 쓰레드로컬에 커넥션이 있다면 그걸 가져온다.

transactionManager.getTransaction()은 트랜잭션을 시작하는 것인데, 이 때 커넥션을 얻어온 다음, 그걸 쓰레드로컬에 저장한다.

그 다음 만약 다시 커넥션을 얻어올 일이 있다면, 먼저 여기 쓰레드로컬부터 뒤진다.

트랜잭션은 이 트랜잭션이 끝날 때 쓰레드로컬에서 커넥션을 삭제한다.(쓰레드로컬에서 없에버린다는 거임. 커넥션을 부수는 게 아니라.) 

그러기 때문에, 트랜잭션 종료전까지 같은 커넥션(DB의 같은 세션)을 유지하는게 가능하다.

 

커밋하기 전까지는, 자기 세션에서만 반영이 되는거다.

그래서 자기 세션에서는 조회해도 남들 커넥션 세션에서는 조회 못한다.

 

여튼 commit되면 그 자기세션의 상태를 본격적으로 DB에 반영시키는 건데,

롤백 시켜버리니 그냥 원래 상태로 돌아와 버린다.

 

TransactionStatus는 뭔가 트랜잭션의 설정상태에 관한 것을 담고 있는 것 같다.

그래서 rollback()할 때도 롤백 이후 어떻게 할 건지 (예를 들면 오토커밋 모드를 다시 킨다던지) 알아야 하기 때문에 인자로 들어가는 것 같다.

'스프링 > 6. 스프링 DB-2' 카테고리의 다른 글

16. 임베디드 모드 DB  (0) 2023.10.09
15. @Transactional로 테스트  (0) 2023.10.09
13. 테스트용 DB와 서버용 DB 분리  (0) 2023.10.09
12. DB 접근 기술 테스트 하는 법  (0) 2023.10.09
11. JDBC 기능들  (0) 2023.10.08