처음 구독하던 곳에서 받을 메일중에 link 읽던중 관련된 것들을 정리할 필요성이 있어서 정리하게 되었다.
말그대로 fetch 방법을 결정하는 속성인데 EAGER, LAZY 두가지가 있다.
EAGER는 '열렬한' 단어 뜻 그대로 조회시 연관된 객체도 함께 Fetch하는 것이고
LAZY는 '게으른' 단어 뜻 그대로 조회시 연관된 객체는 함께 Fetch 하지 않는다.
기본적으로 @XXXToOne(@ManyToOne, @OneToOne)에는 FetchType.EAGER가 @XXXToMany(@ManyToMany, @OneToMany)에는 FetchType.LAZY 디폴트로 설정된다.

확인을 하기위한 ERD이다. member마다 team_id를 이용하여 team을 참조하도록 했다.
public class Member {
@Id
Long memberId;
String memberName;
@ManyToOne(fetch속성을 명시하겠다
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id")
Team team;
}
위에서 적은대로 Team은 @ManyToOne이므로 디폴트값이 FetchType.EAGER이긴 하지만 이해를 위해 fetch속성을 명시하겠다.
public class Team {
@Id
Long teamId;
String teamName;
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
List<Member> memberList;
}
memberList도 마찬가지 @OneToMany이므로 FetchType.LAZY이다.
@DataJpaTest를 이용하여 진행하였다

select m1_0.member_id,m1_0.member_name,m1_0.team_id from tb_member m1_0
member 엔티티를 findall 했으므로 당연히 실행돼야 한다.
select t1_0.team_id,t1_0.team_name from tb_team t1_0 where t1_0.team_id=?
member의 team_id를 조회하기 위한 쿼리를 한번 더 수행한다.
public class Team {
@OneToMany(mappedBy = "team", fetch = FetchType.EAGER)
List<Member> memberList;
}
해당 부분이다. EAGER로 변경해본다.

select m1_0.member_id,m1_0.member_name,m1_0.team_id from tb_member m1_0는 당연히 실행된다.
하지만 밑에 줄을 보면
select t1_0.team_id,t1_0.team_name,ml1_0.team_id,ml1_0.member_id,ml1_0.member_name from tb_team t1_0 left join tb_member ml1_0 on t1_0.team_id=ml1_0.team_id where t1_0.team_id=?
팀마다 속하는 member를 구하기 위해 join을 더 수행한다. 팀원, 팀이 많다면 정말 낭비가 아닐까 한다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
Team team;
}
해당부분이다. FetchType.LAZY로 변경한다.

select m1_0.member_id,m1_0.member_name,m1_0.team_id from tb_member m1_0 한번만 실행된다.
@Query("select m from Member m join fetch m.team")
List<Member> findAllFetchJoin();
위와 같이 메소드를 분리해놓고 해당 메소트 호출시에만 이용하는 것이다.

한번의 쿼리 수행으로 Team 정보를 얻을 수 있다.
@BatchSize(size = 3)
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
List<Member> memberList;
위와같이 BatchSize를 3으로 설정해보았다.
for (int i = 0; i < 2; i++) {
Team teamRow = teamList.get(i);
teamRow.getMemberList().forEach(member -> {
System.out.println("선수이름 : "+member.getMemberName() + " 소속팀 : " +member.getTeam().getTeamName());
});
}
test에서 팀을 2개만 Memberlist를 찍도록 했다.

log를 살펴보면 array [1, 2, 3]이 파라미터로 binding됐다. 이렇게 해서 N+1을 방지하자는 뜻인데
//@BatchSize(size = 3)
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
List<Member> memberList;
@BatchSize없이 실행해보면

그냥 1 ,2개의 memberlist만 select해서 fetch시키므로 n+1을 걱정하진 않아도 될 거 같다.
그래서 @BatchSize 미리 설정을 함으로써 방지하는 역할이 아닐까 하지만 FetchType.EAGER를 사용하면 @BatchSize상관없이 전체를 fetch 해버린다. 심지어 난 2개의 memberlist만 출력했지만 @BatchSize가 미리 3으로 설정돼있어서 필요없는 3도 같이 fetch된 것도 있다.
추후 더 알아내는 것이 있으면 수정하겠다.
데이터 양이 적다면 N+1은 큰문제가 아닐수도 있다, 하지만 필요없이 불러와지는 데이터가 크다면 이는 분명히 낭비이다.