User Table
@Entity(name = "users")
@Getter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String firstName;
private String lastName;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
@Entity
@Getter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long teamId;
private String name;
@OneToMany(mappedBy = "team")
private List<User> users = new ArrayList<>();
}
INSERT INTO team (team_id, name)
VALUES(1, 'A'), (2, 'B'), (3, 'C');
INSERT INTO users (user_id, first_name, last_name, team_id)
VALUES(1, 'A', 'A', 1),
(2, 'B', 'B', 2),
(3, 'C', 'C', 3);
//Team Table
@OneToMany(mappedBy = "team", fetch = FetchType.EAGER)
public void getTeams() {
log.info("--------------------FindTeam--------------------");
List<Team> all = teamRepository.findAll();
log.info("--------------------end--------------------");
}
INFO 2491 --- [nio-8080-exec-1] com.test.test.team.TeamService : --------------------FindTeam--------------------
[Hibernate]
select
team0_.team_id as team_id1_0_,
team0_.name as name2_0_
from
team team0_
[Hibernate]
select
users0_.team_id as team_id4_1_0_,
users0_.user_id as user_id1_1_0_,
users0_.user_id as user_id1_1_1_,
users0_.first_name as first_na2_1_1_,
users0_.last_name as last_nam3_1_1_,
users0_.team_id as team_id4_1_1_
from
users users0_
where
users0_.team_id=?
[Hibernate]
select
users0_.team_id as team_id4_1_0_,
users0_.user_id as user_id1_1_0_,
users0_.user_id as user_id1_1_1_,
users0_.first_name as first_na2_1_1_,
users0_.last_name as last_nam3_1_1_,
users0_.team_id as team_id4_1_1_
from
users users0_
where
users0_.team_id=?
[Hibernate]
select
users0_.team_id as team_id4_1_0_,
users0_.user_id as user_id1_1_0_,
users0_.user_id as user_id1_1_1_,
users0_.first_name as first_na2_1_1_,
users0_.last_name as last_nam3_1_1_,
users0_.team_id as team_id4_1_1_
from
users users0_
where
users0_.team_id=?
INFO 2491 --- [nio-8080-exec-1] com.test.test.team.TeamService : --------------------end--------------------
//Team Table
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
public void getTeams() {
log.info("--------------------FindTeam--------------------");
List<Team> all = teamRepository.findAll();
log.info("--------------------end--------------------");
}
INFO 2527 --- [nio-8080-exec-2] com.test.test.team.TeamService : --------------------FindTeam--------------------
[Hibernate]
select
team0_.team_id as team_id1_0_,
team0_.name as name2_0_
from
team team0_
INFO 2527 --- [nio-8080-exec-2] com.test.test.team.TeamService : --------------------end--------------------
해당 상황에서는 N+1 문제가 발생하지 않는 것처럼 보인다.
그러나 team의 users를 사용하려고 하면 문제가 발생한다.
Service
public void getTeamsUseUsers() {
log.info("--------------------FindTeamUseUsers--------------------");
List<Team> all = teamRepository.findAll();
for (Team team : all) {
List<User> users = team.getUsers();
for (User user : users) {
user.getUserId();
}
}
log.info("--------------------end--------------------");
}
INFO 2556 --- [nio-8080-exec-1] com.test.test.team.TeamService : --------------------FindTeamUseUsers--------------------
[Hibernate]
select
team0_.team_id as team_id1_0_,
team0_.name as name2_0_
from
team team0_
[Hibernate]
select
users0_.team_id as team_id4_1_0_,
users0_.user_id as user_id1_1_0_,
users0_.user_id as user_id1_1_1_,
users0_.first_name as first_na2_1_1_,
users0_.last_name as last_nam3_1_1_,
users0_.team_id as team_id4_1_1_
from
users users0_
where
users0_.team_id=?
[Hibernate]
select
users0_.team_id as team_id4_1_0_,
users0_.user_id as user_id1_1_0_,
users0_.user_id as user_id1_1_1_,
users0_.first_name as first_na2_1_1_,
users0_.last_name as last_nam3_1_1_,
users0_.team_id as team_id4_1_1_
from
users users0_
where
users0_.team_id=?
[Hibernate]
select
users0_.team_id as team_id4_1_0_,
users0_.user_id as user_id1_1_0_,
users0_.user_id as user_id1_1_1_,
users0_.first_name as first_na2_1_1_,
users0_.last_name as last_nam3_1_1_,
users0_.team_id as team_id4_1_1_
from
users users0_
where
users0_.team_id=?
INFO 2556 --- [nio-8080-exec-1] com.test.test.team.TeamService : --------------------end--------------------
SELECT * FROM team AS t JOIN member AS m ON t.team_id = m.team_id
JPQL에서 성능 최적화를 위해 기존 SQL 조인 종류가 아닌 fetch join를 제공
N+1 문제를 해결하기 위해 연관된 엔티티를 한 번에 조회하는 Fetch Join 쿼리를 사용 가능
연관된 엔티티나 컬렉션을 SQL 한번에 조회가 가능
아래와 같이 Team과 연관된 Users를 함께 조회
Repsitory
@Query("select t from Team t join fetch t.users")
fetch join은 지연로딩으로 설정해놓아도 우선순위를 가지기 때문에 즉시 로딩으로 동작
Log
INFO 3213 --- [nio-8080-exec-4] com.test.test.team.TeamService : --------------------UseFetchJoin--------------------
[Hibernate]
select
team0_.team_id as team_id1_0_0_,
users1_.user_id as user_id1_1_1_,
team0_.name as name2_0_0_,
users1_.first_name as first_na2_1_1_,
users1_.last_name as last_nam3_1_1_,
users1_.team_id as team_id4_1_1_,
users1_.team_id as team_id4_1_0__,
users1_.user_id as user_id1_1_0__
from
team team0_
inner join
users users1_
on team0_.team_id=users1_.team_id
INFO 3213 --- [nio-8080-exec-4] com.test.test.team.TeamService : --------------------end--------------------
Team
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
@BatchSize(size = 3)
private List<User> users = new ArrayList<>();
INFO 3552 --- [nio-8080-exec-1] com.test.test.team.TeamService : --------------------FindTeamUseUsers--------------------
[Hibernate]
select
team0_.team_id as team_id1_0_,
team0_.name as name2_0_
from
team team0_
[Hibernate]
select
users0_.team_id as team_id4_1_1_,
users0_.user_id as user_id1_1_1_,
users0_.user_id as user_id1_1_0_,
users0_.first_name as first_na2_1_0_,
users0_.last_name as last_nam3_1_0_,
users0_.team_id as team_id4_1_0_
from
users users0_
where
users0_.team_id in (
?, ?, ?
)
INFO 3552 --- [nio-8080-exec-1] com.test.test.team.TeamService : --------------------end--------------------