금일 JPA 스터디에서 알게된 내용들 !

스터디에서 알게된 내용들

을 기입하였습니다 ㅎㅎ


  • 오늘 발표자 빡쌤님의 말씀에 의하면

테스트코드 클래스레벨에 @Transactional을 걸어두었을 떄 조심해야 한다

Service의 @Transactional이 무시될 수 있다!


  • @AfterEach 보다는 @BeforeEach !

왜냐하면 각 테스트 메서드에서 어떤 한 test가 fail이 나면 그 뒤에 수행되는 @AfterEach 메서드는 실행이 안될 수 있기 때문이다 !

  • 그러나 실험 결과 잘만 실행되는데.. !! 으음?!

상황에 따라 달라지는 객체의 동등성

아래의 경우는 당연히 같다

@Transactional
@Test
public void 저장한_객체와_꺼낸_객체는_당연히_같다() {

	// when
	Long savedId = memberService.join(member);
	Member findMember = memberService.findOne(savedId);

	// then
	assertThat(findMember).isEqualTo(member);
}

그러나 다음의 경우는 다르다

@Test
public void 영속성_컨텍스트가_다를경우_객체는_서로_같지않다() {

	// when
	// 영속성 컨텍스트_시작 --->
	Long savedId = memberService.join(member);
	// <--- 영속성 컨텍스트 종료

	// 영속성 컨텍스트_시작 --->
	Member findMember = 회원_찾기(savedId);
	// <--- 영속성 컨텍스트 종료

	// then
	assertThat(findMember == member).isFalse();
	assertThat(findMember).isNotEqualTo(member);
}

@Transactional
public Member 회원_찾기(Long id) {
	return memberService.findOne(id);
}

우선 회원_찾기 메서드는 별도로 트랜잭션이 걸려있어서 새로운 트랜잭션이 열린다. 또한 memberService또한 메서드에 트랜잭션이 걸려있어서 별도의 트랜잭션을 가진다

따라서 JPA에서 두 객체는 서로 다른 객체이다

@Transactional
@Test
public void 영속성_컨텍스트가_다를경우_객체는_서로_같지_않지만_동일한_트랜잭션_범위라면_서로같다() {

	// when
	Long savedId = memberService.join(member);
	Member findMember = 회원_찾기(savedId);

	// then
	assertThat(findMember == member).isTrue();
	assertThat(findMember).isEqualTo(member);
}

방금 위에서 시행했었던 테스트 메서드 위에 @Transactional을 붙이면 pass가 된다

이유는 스프링의 기본 @Tranasactional 하위 트랜잭션이 상위에 참여를 하기 때문에 각각의 트랜잭션은 의미가 없어진다

반대로

@Transactional(propagation = Propagation.REQUIRES_NEW)

자식 트랜잭션들에 위와 같이 설정을 하게 되면 자식들마다 새로운 트랜잭션을 열게 되므로 테스트는 fail이 일어나게 된다


다음 실험은 1차캐시를 비웠을 경우이다

@Transactional
@Test
public void _1차_캐시를_지우면_두_객체의_동일성은_서로_다르다() {
	em.persist(member);
	Long savedId = member.getId();
	em.flush();
	em.clear();

	Member findMember = em.find(Member.class, savedId);
	assertThat(findMember != member).isTrue();
}

캐시를 비운 후 찾아오면 두 객체는 서로 다르다 !


  • 빡샘님 : 테스트 코드도 중복을 제거하는 방향으로 최대한 리펙터링한다
    • 예를들어 회원 객체 생성을 매 메서드마다 작성하고 있으면 공통 필드로 추출한다

ordi님 @Transactional(readonly = true)가 왜 빠를까??

코드 레벨로 내려가다 보니까 FlushMode.MANUAL 로 설정되어있는데 더티체킹을 하지 않는다고 한다 !

또한 더티체킹을 하지 않으므로 1차 캐시에 스냅샷을 저장하지도 않는다 !


jpa에서의 동등성 문제


한때 우리 스터디원 빡샘님이 jpa 동등성 이슈 떄문에 6시간동안 디버깅을 한 적이 있다

equals 메서드에 아래와 같이 Hibernate.getClass()로 감싸준다!

이 경우 프록시이거나 진또배기 객체의경우 모두를 케어해주는 것 같다 자세한건 찾아보아야한다!

Hibernate.getClass()




© 2020.12. by 따라쟁이

Powered by philz