Hibernate:
/* <criteria> */ select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
u1_0.profile_id,
u1_0.updated_at,
u1_0.username
from
users u1_0
Hibernate:
select
bc1_0.id,
bc1_0.content_type,
bc1_0.created_at,
bc1_0.file_name,
bc1_0.size
from
binary_contents bc1_0
where
bc1_0.id=?
Hibernate:
select
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
us1_0.user_id
from
user_statuses us1_0
where
us1_0.user_id=?
Hibernate:
select
bc1_0.id,
bc1_0.content_type,
bc1_0.created_at,
bc1_0.file_name,
bc1_0.size
from
binary_contents bc1_0
where
bc1_0.id=?
Hibernate:
select
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
us1_0.user_id
from
user_statuses us1_0
where
us1_0.user_id=?
Hibernate:
select
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
us1_0.user_id
from
user_statuses us1_0
where
us1_0.user_id=?
List<User> users = userRepository.findAll();
public class User {
@OneToOne
@JoinColumn(name = "profile_id")
private BinaryContent profile;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private UserStatus userStatus;
}
@Query("select distinct u from User u left join fetch u.profile left join fetch u.userStatus")
List<User> findAllFetch();
Hibernate:
/* select
u
from
User u
left join
fetch
u.profile
left join
fetch
u.userStatus */ select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
p1_0.id,
p1_0.content_type,
p1_0.created_at,
p1_0.file_name,
p1_0.size,
u1_0.updated_at,
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
u1_0.username
from
users u1_0
left join
binary_contents p1_0
on p1_0.id=u1_0.profile_id
left join
user_statuses us1_0
on u1_0.id=us1_0.user_id
Hibernate:
/* <criteria> */ select
m1_0.id,
m1_0.author_id,
m1_0.channel_id,
m1_0.content,
m1_0.created_at,
m1_0.updated_at
from
messages m1_0
left join
channels c1_0
on c1_0.id=m1_0.channel_id
where
c1_0.id=?
order by
m1_0.created_at
fetch
first ? rows only
Hibernate:
select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
p1_0.id,
p1_0.content_type,
p1_0.created_at,
p1_0.file_name,
p1_0.size,
u1_0.updated_at,
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
u1_0.username
from
users u1_0
left join
binary_contents p1_0
on p1_0.id=u1_0.profile_id
left join
user_statuses us1_0
on u1_0.id=us1_0.user_id
where
u1_0.id=?
Hibernate:
select
c1_0.id,
c1_0.created_at,
c1_0.description,
c1_0.name,
c1_0.type,
c1_0.updated_at
from
channels c1_0
where
c1_0.id=?
Hibernate:
select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
p1_0.id,
p1_0.content_type,
p1_0.created_at,
p1_0.file_name,
p1_0.size,
u1_0.updated_at,
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
u1_0.username
from
users u1_0
left join
binary_contents p1_0
on p1_0.id=u1_0.profile_id
left join
user_statuses us1_0
on u1_0.id=us1_0.user_id
where
u1_0.id=?
Hibernate:
select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
p1_0.id,
p1_0.content_type,
p1_0.created_at,
p1_0.file_name,
p1_0.size,
u1_0.updated_at,
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
u1_0.username
from
users u1_0
left join
binary_contents p1_0
on p1_0.id=u1_0.profile_id
left join
user_statuses us1_0
on u1_0.id=us1_0.user_id
where
u1_0.id=?
Hibernate:
select
a1_0.message_id,
a1_1.id,
a1_1.content_type,
a1_1.created_at,
a1_1.file_name,
a1_1.size
from
message_attachments a1_0
join
binary_contents a1_1
on a1_1.id=a1_0.attachment_id
where
a1_0.message_id=?
Hibernate:
select
a1_0.message_id,
a1_1.id,
a1_1.content_type,
a1_1.created_at,
a1_1.file_name,
a1_1.size
from
message_attachments a1_0
join
binary_contents a1_1
on a1_1.id=a1_0.attachment_id
where
a1_0.message_id=?
Hibernate:
select
a1_0.message_id,
a1_1.id,
a1_1.content_type,
a1_1.created_at,
a1_1.file_name,
a1_1.size
from
message_attachments a1_0
join
binary_contents a1_1
on a1_1.id=a1_0.attachment_id
where
a1_0.message_id=?
public class Message {
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "message_attachments",
joinColumns = @JoinColumn(name = "message_id"),
inverseJoinColumns = @JoinColumn(name = "attachment_id")
)
private List<BinaryContent> attachments;
@ManyToOne
@JoinColumn(name = "channel_id", nullable = false)
private Channel channel;
@ManyToOne
@JoinColumn(name = "author_id")
private User author;
List<Message> messages = messageRepository.findAll();
@Query("select distinct m from Message m join fetch m.author left join fetch m.attachments left join fetch m.channel where m.channel.id = :channelId")
Slice<Message> findAllByChannelIdFetch(@Param("channelId") UUID channelId, Pageable pageable);
Hibernate 5
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
Hibernate 6
지금 같은 경우에는 Hibernate 6를 사용하기 때문에, Fetch Join + Pagination이 실행되긴 하지만 N+1 문제가 해결되지 않음
(아무리 찾아봐도 왜 그런지는 나오지 않았지만, 결과가 여전히 N+1)
=> 이럴 때 @BatchSize를 사용하면 N+1로 인한 성능 저하를 방지할 수 있다.
(김영한 쌤 말씀으로는, 페이징 + @toMany 조회의 유일한 방법이라 하심)
spring:
jpa:
properties:
hibernate.default_batch_fetch_size: 100
Hibernate:
/* <criteria> */ select
m1_0.id,
m1_0.author_id,
m1_0.channel_id,
m1_0.content,
m1_0.created_at,
m1_0.updated_at
from
messages m1_0
left join
channels c1_0
on c1_0.id=m1_0.channel_id
where
c1_0.id=?
order by
m1_0.created_at
fetch
first ? rows only
Hibernate:
select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
p1_0.id,
p1_0.content_type,
p1_0.created_at,
p1_0.file_name,
p1_0.size,
u1_0.updated_at,
us1_0.id,
us1_0.created_at,
us1_0.last_active_at,
us1_0.updated_at,
u1_0.username
from
users u1_0
left join
binary_contents p1_0
on p1_0.id=u1_0.profile_id
left join
user_statuses us1_0
on u1_0.id=us1_0.user_id
where
u1_0.id = any (?)
Hibernate:
select
c1_0.id,
c1_0.created_at,
c1_0.description,
c1_0.name,
c1_0.type,
c1_0.updated_at
from
channels c1_0
where
c1_0.id=?
Hibernate:
select
a1_0.message_id,
a1_1.id,
a1_1.content_type,
a1_1.created_at,
a1_1.file_name,
a1_1.size
from
message_attachments a1_0
join
binary_contents a1_1
on a1_1.id=a1_0.attachment_id
where
a1_0.message_id = any (?)