연관 관계에서 발생하는 이슈로, 연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 개수(n)만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다.
엔티티
@Entity
@Getter
@Setter
@NoArgsConstructor
public clss Owner{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
//LAZY여도 N+1 발생 시점을 연관관계 데이터를 사용하는 시점으로 미룰지,
//아니면 초기 데이터 로드 시점에 가져오느냐에만 차이가 있다.
**@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
private Set<Cat> cats = new LinkedHashSet<>();**
...
}
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Cat{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
**@ManyToOne
private Owner owner;**
public Cat(String name) {
this.name = name
}
}
@Test
void exampleTest(){
Set<Cat> cats = new LinkedHashSet<>();
for(int i=0;i<10;i++){
cats.add(new Cat("cat"+i));
}
catRepository.saveAll(cats);
List<Owner> owners = new ArrayList<>();
for(int i=0;i<10;i++){
Owner owner = new Owner("owner"+i);
owner.setCats(cats);
owners.add(owner);
}
ownerRepository.saveAll(owners);
entityManager.clear();
//test
**List<Owner> everyOwners = ownerRepository.findAll();**
assertFalse(everyOwners.isEmpty());
}
→ 고양이 집사를 조회하는 쿼리를 호출하였다. (1)
→ 고양이를 조회하는 쿼리가 고양이 집사를 조회한 row만큼 호출된 것을 확인할 수 있다. (N)
jpaRepository에 정의한 인터페이스 메소드를 실행하면 JPA는 메소드 이름을 분석 후 JPQL을 생성하여 실행한다.
따라서 JPQL은 findAll()이란 메소드를 수행하였을 때 해당 엔티티를 조회하는 select * from Owner 쿼리만 수행하게 되는 것이다.
⇒ 연관된 엔티티 데이터가 필요한 경우, FetchType으로 지정한 시점에 조회를 별도로 호출하게 된다.