[JPA] 영속성 컨텍스트 맛보기

Tae Woo Kim·2024년 7월 22일

Java

목록 보기
4/6

전편: [JUnit5] @BeforeEach 활용

email unique constraint을 줬는데 저장된 testUser들이 똑같은 email을 가지고 있다.

setUp 메소드:

    @BeforeEach
    void setUp() {
        testUser = User.builder()
                .email("test@example.com")
                .password("password")
                .role(Role.USER)
                .build();
        testUser = userRepository.save(testUser);
        System.out.println("testUser = " + testUser); // 여기서 유저 로깅
    }

로그:

testUser = User(id=1, email=test@example.com, password=password, nick=null, isDeleted=false, createdAt=null, role=USER, tokens=null, entries=null, lists=null, labels=null)
testUser = User(id=2, email=test@example.com, password=password, nick=null, isDeleted=false, createdAt=null, role=USER, tokens=null, entries=null, lists=null, labels=null)
testUser = User(id=3, email=test@example.com, password=password, nick=null, isDeleted=false, createdAt=null, role=USER, tokens=null, entries=null, lists=null, labels=null)

왜 그러지?

By default, tests annotated with @DataJpaTest are transactional and roll back at the end of each test.

스프링 공식문서에 따르면 @DataJpaTest 어노테이션이 붙을 시 각 메소드 종료될 때마다 롤백을 시킨다고 한다.

의문점

각 메소드가 끝날 때 롤백은 이해됐음..
근데 왜 같은 메소드 안에서 똑같은 email을 2번 저장해도 에러가 발생하지 않는 것이지?
어떻게 돌아가는지 이해가 부족해서 오는 문제인 것 같은데..

조사 결과

  • userRepository.save(testUser)를 실행하면 Persistance Context (영속성 컨텍스트)에서 변화를 주지만 실제로 DB에 입력되지 않는다.

  • 실제로 DB에 입력되는 지점은 해당 트랜젝션이 커밋이 될 때인데, 커밋이 되면 flush()를 자동으로 해준다.

  • flush()를 해줄 시 트랜젝션 안 속 Persistance Context 내에 있던 쿼리들이 실제로 반영되는 것이다.

  • 이때 제약조건 (email) 등이 검사되며 이 때에 Exception이 발생하는 것이다.

이를 뒷받침하기 위해 코드로 테스트해봤다.


	@BeforeEach
    void setUp() {
        testUser = User.builder()
                .email("test@example.com")
                .password("password")
                .role(Role.USER)
                .build();
        testUser = userRepository.save(testUser);
    }


    @Test
    public void EntryRepository_SaveWithoutLabel_ReturnEntry() {
        // given
        User dup = User.builder()
                .email("test@example.com")
                .password("password")
                .role(Role.USER)
                .build();
        dup = userRepository.save(dup); // 똑같은 email을 가진 dup 저장
        userRepository.flush(); // flush

        Entry entry = Entry.builder()
                .title("Title01")
                .content("Content01")
                .user(testUser)
                .build();

        // when
        Entry savedEntry = entryRepository.save(entry);

        System.out.println("savedEntry = " + savedEntry);

        // then
        assertThat(savedEntry).isNotNull();
        assertThat(savedEntry.getId()).isGreaterThan(0);
        assertThat(savedEntry.getUser()).isEqualTo(testUser);
        assertThat(savedEntry.getLabel()).isNull();
    }

똑같은 이메일을 2번 저장해보았다 ("test@example.com")
1. @BeforeEach에서
2. EntryRepository_SaveWithouLabel_ReturnEntry()에서

그런 뒤 userRepository.flush()를 한다.

그랬더니

email 중복됐다고 예외가 발생했다. 이번 기회에 JPA의 Persistance Context와 테스트 환경에대해서 더 알게되어서 기쁘다. 그리고 틀린 부분이 있으면 부담없이 말씀해주셨으면 좋겠다.

0개의 댓글