ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • FetchType.EAGER, FetchType.LAZY
    Spring(Web) 2024. 3. 20. 22:51

    기존에 알고있듯이 @OneToMany와 같은 어노테이션이 붙어있는 필드 엔티티는 FetchType.LAZY가 기본값이고, @ManyToOne과 같은 어노테이션은 FetchType.EAGER가 기본값이다.

    FetchType.EAGER가 붙어있는 필드 엔티티는, 해당 필드를 포함한 엔티티를 조회하는 순간 해당 필드 엔티티도 조회하게 된다.

     

    @xxToOne 연관관계인 필드 엔티티에는 FetchType.LAZY를 붙이는게 대부분 좋다는 것을 김영한님 강의에서도 들어서 알고는 있었는데, 붙이지 않고 구현한 경우가 생겨 트러블 슈팅을 하게 되었다.

     

    지금 개발중인 비즈니스 로직에는 ChatRoom(채팅방 정보), UserChatRoom(채팅방에 참여하고있는 사용자 정보), Chat(채팅 메세지 정보) 총 세개의 엔티티가 있다. ChatRoom - UserChatRoom이 1:N, ChatRoom - Chat도 1:N로 ChatRoom에 물려있는 형태로 구현을 하게 되었다. 결국 모든 엔티티에는 Member가 묶여있게 되는데, 그래서 FetchType.EAGER가 기본값인 @xxToOne 연관관계를 가진 엔티티들을 조회할 때 마다 member를 아래 로그와 같이 100개 가량의 파라미터를 이용해 조회하는 것을 발견하게 되었다.

     

    UserChatRoom.java (수정 전)

    @Entity
    @Getter @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class UserChatRoom extends BaseEntity {
        @Id @GeneratedValue
        private long userChatRoomId;
    
        @JoinColumn(name = "chat_room_id")
        @ManyToOne
        private ChatRoom chatRoom;
    
        @JoinColumn(name = "member_id")
        @ManyToOne
        private Member member;
    
        LocalDateTime lastAccessTime;
    
        @Override
        public String toString() {
            return "UserChatRoom{" +
                    "userChatRoomId=" + userChatRoomId +
                    ", chatRoom=" + chatRoom.getTitle() +
                    ", member=" + member +
                    ", lastAccessTime=" + lastAccessTime +
                    '}';
        }
    }

     

    수정 전 SQL log

    22:35:10.925 [http-nio-8080-exec-2] TRACE org.hibernate.orm.jdbc.bind -- binding parameter (1:BIGINT) <- [1]
    22:35:10.927 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL -- 
        select
            m1_0.member_id,
            m1_0.address,
            m1_0.email,
            m1_0.is_deleted,
            m1_0.nickname,
            m1_0.phone_number,
            m1_0.profile_image,
            m1_0.provider,
            m1_0.refresh_token,
            m1_0.score 
        from
            member m1_0 
        where
            m1_0.member_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 
            and (
                m1_0.is_deleted = false
            )
    Hibernate: 
        select
            m1_0.member_id,
            m1_0.address,
            m1_0.email,
            m1_0.is_deleted,
            m1_0.nickname,
            m1_0.phone_number,
            m1_0.profile_image,
            m1_0.provider,
            m1_0.refresh_token,
            m1_0.score 
        from
            member m1_0 
        where
            m1_0.member_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 
            and (
                m1_0.is_deleted = false
            )

     

    수정 후

        @JoinColumn(name = "chat_room_id")
        @ManyToOne(fetch = FetchType.LAZY)
        private ChatRoom chatRoom;
    
        @JoinColumn(name = "member_id")
        @ManyToOne(fetch = FetchType.LAZY)
        private Member member;

     

    수정 후 SQL log

    22:42:30.461 [http-nio-8080-exec-2] TRACE org.hibernate.orm.jdbc.bind -- binding parameter (1:VARCHAR) <- [02236e5e-eb9e-43b0-9eb3-b54a7f05bc5e]
    22:42:30.462 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL -- 
        select
            m1_0.member_id,
            m1_0.address,
            m1_0.email,
            m1_0.is_deleted,
            m1_0.nickname,
            m1_0.phone_number,
            m1_0.profile_image,
            m1_0.provider,
            m1_0.refresh_token,
            m1_0.score 
        from
            member m1_0 
        where
            m1_0.member_id=? 
            and (
                m1_0.is_deleted = false
            )
    Hibernate: 
        select
            m1_0.member_id,
            m1_0.address,
            m1_0.email,
            m1_0.is_deleted,
            m1_0.nickname,
            m1_0.phone_number,
            m1_0.profile_image,
            m1_0.provider,
            m1_0.refresh_token,
            m1_0.score 
        from
            member m1_0 
        where
            m1_0.member_id=? 
            and (
                m1_0.is_deleted = false
            )

     

     

    결국 대부분의 경우에는, FetchType.LAZY로 구현하는 것이 부하를 줄이고 불필요한 쿼리를 날리지 않게 될 것이다.

    하지만 특정한 경우에는 해당 엔티티에 포함된 다른 엔티티들의 정보를 한 번에 불러와 사용하는것이 성능상/비즈니스 로직 상으로 이득이 될 때가 있다. 잘 생각해서 적용해야 할듯.

Designed by Tistory.