JPA 관련 간단한 실험

김성지·2023년 1월 20일
0

그냥공부

목록 보기
7/10

글 요약은

  1. jpaRepository에서 지원하는 메소드들은
    대부분 @Transactional이 붙어있다.
  2. 영속성컨텍스트에서 변경사항이 감지되면 쓰기 지연 저장소에 저장되어있다가
    commit할 때나 실제 쿼리가 나가게 된다.
    이다.

해보기

Member와 Post를 만들어주기

@Getter
@NoArgsConstructor
@Entity
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY,optional = false)
    private Member member;

    public Post(Member member){
        this.member = member;
    }
}
@Entity
@NoArgsConstructor
@Getter
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String userName;

    @OneToMany(mappedBy = "member",fetch = FetchType.LAZY)
    private List<Post> posts = new ArrayList<>();

    public Member(String userName){
        this.userName = userName;
    }
}

리포지토리는

public interface MemberRepository extends JpaRepository<Member,Long> {
}

public interface PostRepository extends JpaRepository<Post,Long> {
}

서비스는 다음과 같다.

@RequiredArgsConstructor
@Service
public class MemberService {
    private final MemberRepository memberRepository;
    private final PostRepository postRepository;
    @Transactional
    public void deleteMemberById(Long id){
        Member member = memberRepository.findById(id).orElseThrow();
        try{
            memberRepository.delete(member);
        }catch (DataIntegrityViolationException e){
            throw new IllegalArgumentException("여기");
        }
    }
}

테스트코드는 다음과 같다.

@SpringBootTest
@RequiredArgsConstructor
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
public class Test1 {
    private final MemberRepository memberRepository;
    private final PostRepository postRepository;
    private final MemberService memberService;
    @Test
    public void 트랜잭션(){
        Member member = memberRepository.save(new Member("sung_jee"));
        postRepository.save(new Post(member));
        Long memberId = member.getId();

        Assertions.assertThatThrownBy(()->memberService.deleteMemberById(memberId))
                .isInstanceOf(IllegalArgumentException.class);
    }
}

테스트는 실패한다.

이유는 다음과 같다

service 메소드인
deleteMemberById에서 Transaction이 시작된다.!

이때 memberRepository.delete(member)에서
delete함수는 다음처럼 @Transaction이 default값으로 설정되어있다.

기본값인 Required는 부모 트랜잭션있으면 그거 쓰게 된다.
아까 service에서 @Transacional걸어줬으니
이 delete 메소드는 service의 함수랑 생명주기가 같아지게 된다.

이때 쓰기지연 저장소에 기록되고
transcation이 끝날 때 쯤 나가는 실제 쿼리가 나가지 않게 된다.

다음 service 메소드가 끝날 때 나가게 됨..

그래서 DataInegrityViolationException을 받지 못한채로
서비스 함수가 종료될 때 쯤 저 exception이 발생되어
테스트가 실패하게된다.

그래서 서비스 함수위의 @Transactional을 빼주게 되면

delete 함수내에서만 Transaction이 생성 및 진행되어
try catch 내에서 DataInegrityViolationException을 받을 수 있게 된다.

0개의 댓글

관련 채용 정보