@EntityGraph란?
연관관계가 지연 로딩으로 되어있을 경우 fetch 조인을 사용하여 여러 번의 쿼리를 한 번에 해결할 수 있습니다.
@EntityGraph는 Data JPA에서 fetch 조인을 어노테이션으로 사용할 수 있도록 만들어 준 기능입니다.
N+1 발생 이유
기본적으로 @OneToMany, @ManyToMany 관계는 Lazy Loading으로 되어 있는 경우
JPA에서 @OneToMany와 @ManyToOne 관계 등을 지연 로딩(Lazy)으로 설정한 경우, 연관관계에서 종속된 엔티티는 쿼리 실행 시 select 되지 않고 proxy 객체를 만들어 엔티티가 적용시킵니다. 그 후 해당 proxy 객체를 호출할 때마다 그때 그때 select 쿼리가 실행됩니다.
EnttiyGraph 를 사용한 fetch-join
attributePaths 인자에 fetch-join 을 할 대상(필드명)을 작성하여 해당 엔티티들을 fetch-join 할 수 있습니다.
@EntityGraph(attributePaths = "Department")
List<User> findAll();
Repository 클래스 내에, attributePaths 인자에 fetch-join 을 할 대상(필드명)을 작성하여 해당 엔티티들을 fetch-join 할 수 있습니다.
User는 Department와 연관관계를 가지므로, @EntityGraph를 통해 Lazy 로딩을 Eager 로딩으로 변경 가능합니다.
프로젝트 적용기
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Friend extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "fromUser_id", nullable = false)
private User fromUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "toUser_id", nullable = false)
private User toUser;
@Enumerated(EnumType.STRING)
private FriendStatus status;
public void setStatus(FriendStatus status) {
this.status = status;
}
@Builder
private Friend(User fromUser, User toUser, FriendStatus status) {
this.fromUser = fromUser;
this.toUser = toUser;
this.status = status;
}
public static Friend of(User fromUser, User toUser, FriendStatus status) {
return Friend.builder()
.fromUser(fromUser)
.toUser(toUser)
.status(status)
.build();
}
}
User 도메인은 Lazy 전략으로 되어 있어서, proxy 객체로 임시 저장을 하고, 필요할때마다 Select 문이 나갈 수 있다.
@Repository
public interface FriendRepository extends JpaRepository<Friend, Long>, FriendRepositoryCustom {
@EntityGraph(attributePaths = {"fromUser", "toUser"})
Optional<Friend> findFirstByFromUserAndToUserAndStatus(User fromUser, User toUser, FriendStatus status);
}
따라서 @EntityGraph에서 연관관계를 가지는 fromUser, toUser에 있어, EntityGraph를 적용했다.
'프로젝트 > Ku:room' 카테고리의 다른 글
메시지 큐 도입기 - 비동기 로깅 처리 및 알림 기능 (RabbitMQ, Kafka, Redis Pub/Sub 비교) (0) | 2025.03.14 |
---|---|
로그 파일 Grafana+Promtail+loki 이용해서 실시간 모니터링 적용하기 (0) | 2025.03.04 |
Jacoco를 이용한 테스트 커버리지 측정후, 보완하기 (0) | 2025.02.24 |
스프링 인터셉터(Spring Interceptor)로 API로그 DB에 저장하기 (0) | 2025.02.24 |
인터셉터에서 RequestBody를 Read 하지 못하는 이유 및 해결방법 (0) | 2025.02.20 |