@EntityGraph는 처음 듣는 용어이다. 한번 정리해보자.
@Test
public void findMemberLazy() throws Exception {
//given
Team teamA = Team.builder().name("teamA").build();
teamRepository.save(teamA);
Team teamB = Team.builder().name("teamB").build();
teamRepository.save(teamB);
memberJpaRepository.save(Member.builder().age(10).username("member1").team(teamA).build());
memberJpaRepository.save(Member.builder().age(20).username("member2").team(teamB).build());
em.flush();
em.clear();
//when
List<Member> all = memberRepository.findAll();
all.forEach(System.out::println);
//then
}
Member와 Team은 @ManyToOne 관계에 Lazy 로딩이 적용되었다. 따라서 Member가 참조하는 Team 객체는 프락시 객체이고 실제 Team의 값에 접근할 때 추가 쿼리가 발생할 것이다.
Hibernate:
select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
2022-11-27 16:42:54.720 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_] : [BIGINT]) - [1]
2022-11-27 16:42:54.721 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([age2_0_] : [INTEGER]) - [10]
2022-11-27 16:42:54.721 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([team_id4_0_] : [BIGINT]) - [1]
2022-11-27 16:42:54.722 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([username3_0_] : [VARCHAR]) - [member1]
2022-11-27 16:42:54.722 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_] : [BIGINT]) - [2]
2022-11-27 16:42:54.722 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([age2_0_] : [INTEGER]) - [20]
2022-11-27 16:42:54.722 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([team_id4_0_] : [BIGINT]) - [2]
2022-11-27 16:42:54.722 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([username3_0_] : [VARCHAR]) - [member2]
Hibernate:
select
team0_.id as id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.id=?
2022-11-27 16:42:54.734 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2022-11-27 16:42:54.736 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([name2_1_0_] : [VARCHAR]) - [teamA]
Hibernate:
select
team0_.id as id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.id=?
2022-11-27 16:42:54.736 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [2]
2022-11-27 16:42:54.737 TRACE 6840 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([name2_1_0_] : [VARCHAR]) - [teamB]
아 이게 싫다.. 그러면 우리는 fetch join 혹은 Lazy로 설정하고 batchSize 옵션을 통해 성능을 개선한다.
관계마다 주의사항과 즉시로딩을 쓰지 않는 이유 등등.. 이러한 것은 JPA 기초이기 때문에 따로 다루진 않겠다.
여기서 포인트는 다음과 같다.
Spring Data JPA를 사용하면 @Query를 적지 않고 메서드 네이밍을 통해 빠르게 빠르게 사용하고 싶다는 것이다.
근데 여기서 fetch join을 적용할려면 결국 jpql을 작성해야 한다는 것이다.
방법이 없을까?
이때 우리는 @EntityGraph 사용하는 것이다!!!!!!
개꿀..
@Override
@EntityGraph(attributePaths = "team", type = EntityGraph.EntityGraphType.FETCH)
List<Member> findAll();
type의 기본값은 Fetch이기 때문에 해당 속성을 적지 않아도 된다.
이렇게 사용했을 경우 실제 발생되는 쿼리는 다음과 같다.
Hibernate:
select
member0_.id as id1_0_0_,
team1_.id as id1_1_1_,
member0_.age as age2_0_0_,
member0_.team_id as team_id4_0_0_,
member0_.username as username3_0_0_,
team1_.name as name2_1_1_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.id
의도한대로 jpql 없이 객체 그래프를 엮어서 성능 최적화로 가져왔다.!!
@EntityGraph는 다양한 조합으로 사용할 수 있다.
@Query에 fetch join없이 사용하고 @EntityGraph을 통해 처리해도 되고 쿼리 메서드에도 @EntityGraph을 사용해서 관계된 엔티티를 가져올 수 있다.
사실 이는 JPA의 기본 스펙이다
@NamedEntityGraph(name = "Member.all", attributeNodes = @NamedAttributeNode("team"))
@EntityGraph("Member.all")
List<Member> findAll();
매번 fetch 해야할 때마다 jpql을 적어주는 것이 귀찮았는데 @EntityGraph를 활용하여 리팩터링 해야겠다.!!
'Spring|Spring-boot > Spring-Data-JPA' 카테고리의 다른 글
Custom Repository (0) | 2022.11.30 |
---|---|
JPA Hint & Lock (0) | 2022.11.27 |
벌크성 수정 쿼리 (0) | 2022.11.27 |
스프링 데이터 JPA (0) | 2022.11.26 |
Spring-Data-JPA 소개 (0) | 2022.10.20 |
댓글