인프런 커뮤니티 질문&답변

gusdn85554님의 프로필 이미지
gusdn85554

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

select 쿼리 발생

작성

·

224

0

영한님 서포터즈님들 안녕하세요 

프로젝트를 하다가 갑자기 궁금한 점이 생겨 테스트를 만들어보고 
제가 현재 어느 부분에서 헤매고 있는지 몰라서 질문드립니다,,

```java

@Entity
public class Team {

@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;

private String name;

@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}

@Entity
public class Member{

@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;

public void addTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}


Main 부분
Team team1 = new Team();
team1.setName("team1");
em.persist(team1);

Member member1 = new Member();
member1.setName("member1");
Member member2 = new Member();
member2.setName("member2");
Member member3 = new Member();
member3.setName("member3");

member1.addTeam(team1);
member2.addTeam(team1);
member3.addTeam(team1);

em.persist(member1);
em.persist(member2);
em.persist(member3);

em.flush();
em.clear();

Team team = em.find(Team.class, team1.getId());

for (Member member : team.getMembers()) {
//select 쿼리가 member의 개수만큼 나가야한다고 생각
System.out.println("member.getName() = " + member.getName());
}

```

db에도 잘 저장되있습니다.

 

제가 궁금한 점은 주석과 같이 member.getName을 사용하게 되면 member 수만큼 쿼리가 나가야한다고 생각이 드는데 제가 어느 개념을 놓치고 있는지 너무 궁금합니다.. 

프록시 부분을 다시 봐도 member의 실제 값을 사용하는데 어떻게 List<Member> members를 한 번에 조회해오지? 라는 생각이 듭니다,,

혹시 어느 부분을 놓쳤는지 말씀해주시면 바로 공부해보도록 하겠습니다

감사합니다

추가로 

```java

Team team = em.getReference(Team.class, team1.getId());

```

제가 프록시와 헷갈려서 위와 같이 코드를 작성하고 다시 해봤는데도 똑같습니다,,

 

답변 4

0

gusdn85554님의 프로필 이미지
gusdn85554
질문자

안녕하세요 영한님 답변 감사합니다.

다만 N+1 문제가 발생하려면 처음에 조회하는 member 자체가 지금처럼 하나가 아니라 둘 이상이어야 합니다.라는게 무슨 말씀이신지 이해가 되질 않습니다

members에 있는 member들은 한번에  실제 객체를 조회하여서  N+1이 발생하지 않는 것이다라고 이해를 했습니다.

JPA2에서 가르쳐주신 Order과 Member와의 관계를 연관지어서 생각해봤는데,  Order를 1번 조회하면 Member가 N번만큼 조회되어 N+1이 발생된다. 이 때는 N -> 1로 조회했기 때문에 N+1이 발생한 이유는 이해됐습니다.

Team -> Member는 1 -> N으로 조회되는데 member 자체가 둘 이상이 되어야 N+1문제가 발생한다
이 말씀을 잘 모르겠습니다

감사합니다.

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. gusdn85554님

제가 설명을 잘못드렸네요. member가 아니라 team을 복수로 조회하시면 됩니다.

Team team = em.find(Team.class, team1.getId()); 이렇게 조회하는 대신에

JPQL을 사용해서 team을 리스트로 조회해서 테스트해보시면 이해가 되실거에요.

감사합니다.

 

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. gusdn85554님

이 경우에는 1번만 조회되는 것이 맞습니다.

team.getMembers()를 채우기 위해 지연로딩으로 실행되는 쿼리를 보시면 이해가 되실거에요.

    select

        members0_.TEAM_ID as team_id10_6_0_,

        members0_.MEMBER_ID as member_i1_6_0_,

        members0_.MEMBER_ID as member_i1_6_1_,

        members0_.createdBy as createdb2_6_1_,

        members0_.createdDate as createdd3_6_1_,

        members0_.lastModifiedDate as lastmodi4_6_1_,

        members0_.lastmodifiedBy as lastmodi5_6_1_,

        members0_.city as city6_6_1_,

        members0_.name as name7_6_1_,

        members0_.street as street8_6_1_,

        members0_.TEAM_ID as team_id10_6_1_,

        members0_.zipcode as zipcode9_6_1_ 

    from

        Member members0_ 

    where

        members0_.TEAM_ID=?

이미 해당 팀에 소속된 모든 회원 정보를 한번에 조회하게 됩니다. 여기서 핵심은 team -> members의 관계는 프록시 이지만, members 자체는 쿼리 한번에 조회할 수 있다는 점입니다.

N+1 문제가 발생하려면 처음에 조회하는 member 자체가 지금처럼 하나가 아니라 둘 이상이어야 합니다.

감사합니다.

 

0

gusdn85554님의 프로필 이미지
gusdn85554
질문자

안녕하세요 영한님

제가 sql 을 안올렸었네요,, 죄송합니다

select

        team0_.TEAM_ID as TEAM_ID1_10_0_,

        team0_.name as name2_10_0_ 

    from

        Team team0_ 

    where

        team0_.TEAM_ID=?

team.getClass() = class jpabook.domain.Team

Hibernate: 

    select

        members0_.TEAM_ID as TEAM_ID10_6_0_,

        members0_.MEMBER_ID as MEMBER_I1_6_0_,

        members0_.MEMBER_ID as MEMBER_I1_6_1_,

        members0_.createdBy as createdB2_6_1_,

        members0_.createdDate as createdD3_6_1_,

        members0_.lastModifiedDate as lastModi4_6_1_,

        members0_.lastmodifiedBy as lastmodi5_6_1_,

        members0_.city as city6_6_1_,

        members0_.name as name7_6_1_,

        members0_.street as street8_6_1_,

        members0_.TEAM_ID as TEAM_ID10_6_1_,

        members0_.zipcode as zipcode9_6_1_ 

    from

        Member members0_ 

    where

        members0_.TEAM_ID=?

member = jpabook.domain.Member@656922a0

member.getName() = member1

member = jpabook.domain.Member@656922a0

member = jpabook.domain.Member@5922d3e9

member.getName() = member2

member = jpabook.domain.Member@5922d3e9

member = jpabook.domain.Member@7d57dbb5

member.getName() = member3

member = jpabook.domain.Member@7d57dbb5

 

sql 쿼리 입니다,

 

https://drive.google.com/file/d/1TE6P3omeTuFu6VyJdcQ_p9nttD5cz5AS/view?usp=sharing

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. gusdn85554님

1. 실행 결과 로그를 자세히 남겨주세요. 특히 실행되는 모든 sql을 남겨주세요.

2. 전체 프로젝트를 압축해서 구글 드라이브로 공유해서 링크를 남겨주세요.

구글 드라이브 업로드 방법은 다음을 참고해주세요.

https://bit.ly/3fX6ygx

 

주의: 업로드시 링크에 있는 권한 문제 꼭 확인해주세요

 

추가로 다음 내용도 코멘트 부탁드립니다.

1. 실행 방법을 알려주세요.

2. 어떻게 문제를 확인할 수 있는지 자세한 설명을 남겨주세요.

감사합니다.

 

gusdn85554님의 프로필 이미지
gusdn85554

작성한 질문수

질문하기