• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

manytoone testcode 작성

19.03.18 01:23 작성 조회수 244

0

아래와 같이 간단한 entity를 생성한다고 했을때,

테스트코드에서는 한번에 조회가 되지만 실제 webcontroller에서 호출했을때는

member를 조회하고 team을 한번더 조회하게 됩니다.

testcode에서 비슷하게 하려고 member객체를 json으로 출력하게 했는데도

똑똑하게 알아서 채워주고있는데요. 아래 코드를 보면

public class Member {

@Id @GeneratedValue(strategy = GenerationType.AUTO)

Long id;

String name;

@ManyToOne @JoinColumn

Team team;

}

public class Team {

@Id @GeneratedValue(strategy = GenerationType.AUTO)

Long id;

String name;

}

@Test

public void select() throws JsonProcessingException {

Team team = new Team();

team.setName("팀1");

Team saveTeam = teamRepo.save(team);

Member member = new Member();

member.setName("학생1");

member.setTeam(saveTeam);

memberRepo.save(member);

Member member1 = new Member();

member1.setName("학생2");

member1.setTeam(saveTeam);

memberRepo.save(member1);

List<Member> all = memberRepo.findAll();

ObjectMapper mapper = new ObjectMapper();

String jsonInString = mapper.writeValueAsString(all);

System.out.println(jsonInString);

}

wecontroller에서 List<Member>로 데이터 리턴시 n+1조회문제가 발생하는데요

-------

Hibernate:

select

member0_.id as id1_0_,

member0_.name as name2_0_,

member0_.team_id as team_id3_0_

from

member member0_

Hibernate:

select

team0_.id as id1_10,

team0_.name as name2_10

from

team team0

where

team0.id=?

unittest에서는

Hibernate:

select

member0_.id as id10,

member0_.name as name20,

member0_.team_id as team_id30

from

member member0_

[{"id":3,"name":"학생1","team":{"id":1,"name":"팀1"}},{"id":4,"name":"학생2","team":{"id":2,"name":"팀2"}}]

team_name이 조회쿼리에도 없는데도 알아서 데이터를 불러와서 결과를 넣어주는 모습을 보이는데

@DataJpaTest에서 webcontroller 호출하는 것과 동일한 결과를 얻어보려면 어떤식으로 테스트를

해야할까요?..

controller test코드를 작성해야할까요?..

답변 1

답변을 작성해보세요.

0

@DataJpaTest를 사용한 테스트 코드에서 쿼리가 조금 날아간 이유는 트랜잭션 단위가 해당 테스트 메소드여서 그래요. 다시 말해서, 해당 테스트 한덩어리가 하나의 트랜잭션이고, 그 트랜잭션 안에서 save 했던 객체들은 Persistence Context가 알고 있는 객체라서 Team을 다시 조회할 필요는 없었던 것이지만 해당 트랜잭션 안에서 새로 추가한 객체말고 또 DB에 어떤 Member 데이터가 있는지 모르니까 Member를 조회하는 쿼리가 발생해야 했던거죠. 그래서 Member를 select 하는 쿼리만 발생한 것이고.

컨트롤러에서 조회한 경우에는 해당 Member 데이터를 추가하지 않고 조회만 하고 있는 상태이고, 조회할 때 처음 Persistence Context에 들어가고 그리고 관계 맵핑이 @XxxToOne인 경우에는 기본적으로 fetching 옵션이 eager 이기 때문에 Team에 대한 정보도 조회해오게 됩니다. 그래서 말씀하신대로 n+1 select 문제가 발생한거구요.

  1. Member 만 조회하고 싶은 경우에는 Member 클래스 안에 @ManyToOne 애노테이션에 fetch 옵션을 Lazy로 바꾸면 그렇게 할 수도 있습니다. 하지만 그 이후에 해당 Member 객체의 Team 정보에 접근하려할 때 트랜잭션 내부에서 접근하는게 아니라면 아마도 LazyInitializationException 이런 에러를 보게 될겁니다. 이보다 더 자세한 내용은 하이버네이트 또는 JPA를 학습하셔야 합니다. 본 강좌 주제를 많이 벗어나기 때문에 더이상의 설명은 생략하겠습니다.

  2. 테스트 코드에서도 동일한 쿼리를 확인하고 싶다면 동일한 조건으로 테스트 코드를 작성해야 합니다. 데이터를 넣는 일과 조회하는 일을 분리해서 테스트 하세요. 그러려면 트랜잭션을 직접 관리하거나, 미리 테스트용 데이터를 넣어두는 방법을 생각해 볼 수 있을 것 같습니다. 가장 간단한 방법으로는 @DataJpaTest를 쓰지 않고 @SpringBootTest를 쓰면 @Transactional이 제공되지 않기 때문에 컨트롤러 코드를 실행할 때와 동일한 쿼리를 볼 순 있을 겁니다.