🤍 전 강의 25% 할인 중 🤍

2024년 상반기를 돌아보고 하반기에도 함께 성장해요!
인프런이 준비한 25% 할인 받으러 가기 >>

  • 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

상속 구조 내에서 Querydsl로 특정 타입의 엔티티에만 조인

24.05.24 18:41 작성 24.05.24 18:49 수정 조회수 136

0

안녕하세요, 질문이 있어서 글 남기게 되었습니다.

이런식으로 지점(branch)명에 맞는 공간(space)중 미팅룸 리스트를 보여주고
해당 미팅룸의 해당 날짜의 예약들도 보여주는 기능을 구현하고자

    @Override
    public List<Reservation> findReservationListByDateAndBranchName(LocalDateTime startDatetime, LocalDateTime endDateTime, String branchName) {

        return queryFactory
                .select(reservation)
                .from(reservation)
                .join(reservation.space, space).fetchJoin()
                .join(space.branch, branch)
                .where(branch.branchName.eq(branchName),
                        reservation.reservationStartDateTime.between(startDatetime, endDateTime),
                        space.dtype.eq("MeetingRoom")
                        )
                .fetch();
    }

이런 코드를 작성하였는데 아래와 같은 연관관계 때문에


Reservation Entity

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "reservation")
public class Reservation extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "reservation_id")
    private Long reservationId;

    @Column(name = "reservation_start_date_time")
    private LocalDateTime reservationStartDateTime;

    @Column(name = "reservation_end_date_time")
    private LocalDateTime reservationEndDateTime;

    @OneToMany(mappedBy = "reservation")
    private List<MemberReservation> memberReservations = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "space_id")
    private Space space;
}

Space Entity

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "space")
@DiscriminatorColumn
public abstract class Space {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "space_id")
    private Long spaceId;

    @NotNull
    @Column(name = "space_name")
    private String spaceName;

    @NotNull
    @Column(name = "space_floor")
    private int spaceFloor;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "branch_id")
    private Branch branch;

    @OneToMany(mappedBy = "space")
    private List<Reservation> reservations = new ArrayList<>();

    @NotNull
    @Column(name = "dtype", insertable = false, updatable = false)
    private String dtype;
}


Space를 상속받는 FocusDesk, MeetingRoom, RechargingRoom

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "focus_desk")
public class FocusDesk extends Space {
//추후에 해당 엔티티에 맞는 필드들 추가
}
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "meeting_room")
public class MeetingRoom extends Space {

    @Column(name = "meeting_room_capacity")
    @Positive
    private int meetingRoomCapacity;
}
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "recharging_room")
public class RechargingRoom extends Space {
//추후에 해당 엔티티에 맞는 필드들 추가
}

 

select
    r1_0.reservation_id,
    r1_0.created_date,
    r1_0.last_modified_date,
    r1_0.reservation_end_date_time,
    r1_0.reservation_start_date_time,
    s1_0.space_id,
    s1_0.dtype,
    s1_0.branch_id,
    s1_0.space_floor,
    s1_0.space_name,
    s1_2.meeting_room_capacity 
from
    reservation r1_0 
join
    (space s1_0 
left join
    focus_desk s1_1 
        on s1_0.space_id=s1_1.space_id 
left join
    meeting_room s1_2 
        on s1_0.space_id=s1_2.space_id 
left join
    recharging_room s1_3 
        on s1_0.space_id=s1_3.space_id) 
    on s1_0.space_id=r1_0.space_id 
join
    branch b1_0 
        on b1_0.branch_id=s1_0.branch_id 
where
    b1_0.branch_name=? 
    and r1_0.reservation_start_date_time between ? and ? 
    and s1_0.dtype=?

이런 쿼리가 나가게 됩니다.
처음에는 dtype 없이 MeetingRoom 만 join 하고 싶었지만
reservation.meetingRoom 이불가능하여(reservation은 space랑 매핑 되어있으니까)
불가능하여 이방법 밖에는 떠오르지 않았습니다.
문제는 위의 쿼리처럼 쓸모없는 테이블들을 강제로 조인한 후에 다시 where절의 dtype으로 구분을 해야한다는 것입니다.

    @Query("SELECT new com.example.sabujak.reservation.dto.ReservationQueryDto(r.reservationStartDateTime, r.reservationEndDateTime, s.spaceId, s.spaceFloor, s.spaceName, m.meetingRoomCapacity) " +
            "FROM Reservation r " +
            "JOIN r.space s " +
            "JOIN MeetingRoom m ON m.spaceId = s.spaceId " +
            "JOIN s.branch b " +
            "WHERE b.branchName = :branchName " +
            "AND r.reservationStartDateTime BETWEEN :startDateTime AND :endDateTime")

이런식의 jpql로는 해결이 가능하지만
추후에 정렬조건을 동적으로 받아야 하기에 querydsl을 사용해서 최대한 좋은 방향으로 사용하고 싶은데
어떻게 해야할지 잘 모르겠습니다

답변 2

·

답변을 작성해보세요.

1

안녕하세요. 주우민님

이 부분은 InheritanceType.JOINED 방식의 한계입니다.

대안은 다음과 같습니다.

  1. 이 부분을 인정하고 사용한다.

  2. SINGLE_TABLE 방식으로 변경한다.

  3. JPA가 제공하는 상속 관계를 사용하지 않고 직접 연관관계를 만들어서 사용한다.

감사합니다.

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2024.05.24

안녕하세요, 인프런 AI 인턴입니다.

주우민님의 질문은 Querydsl을 사용하여 상속 구조에서 특정 타입에 대한 조인을 수행하고 관련 데이터를 조회하는 것과 관련이 있습니다. 이와 유사한 상황을 다룬 기존 답변을 찾아봤을 때, 정확한 일치 사례를 찾기는 어렵지만, 상속관계있는 엔티티와 연관된 질문에 대한 답변을 찾을 수 있었습니다.


fetch join시 where관련해서 질문드립니다.
- 이 답변에서는 Querydsl을 사용하여 엔티티를 조회할 때 fetch joinwhere 조건을 사용하는 상황을 다루고 있습니다. 특히, XToOne (ManyToOne, OneToOne) 관계에서는 fetch joinwhere 조건을 사용해도 데이터 불일치 문제가 발생하지 않는다는 점이 언급됩니다.
- 링크의 답변은 직접적으로 질문자님이 마주한 상황과 일치하지 않지만, where 조건을 fetch join과 함께 사용할 때 조건의 위치에 따른 데이터 불일치 문제에 대한 이해를 도울 수 있습니다.


그러나 질문자님의 상황은 상속 구조 내에서 특정 타입의 엔티티(MeetingRoom)에만 조인을 하고자 하는 더 복잡한 케이스입니다. Querydsl을 사용할 때, 타입을 기반으로 한 조인을 수행하려면 instanceof 표현이나 @DiscriminatorColumn 값을 이용하여 조건을 명시하는 방법을 고려할 수 있습니다. 하지만, 이러한 방식은 Querydsl 자체적으로 직접 지원하지 않으므로, JPQL이나 Criteria API를 통한 접근 방식을 모색해야 할 수도 있습니다.

조인된 상속 구조의 엔티티에서 특정 타입에 대한 조회를 Querydsl로 구현하고자 하신다면 명시적으로 엔티티 타입을 조건에 포함시키거나, 별도의 처리 방법을 고려해야 할 것으로 보입니다. 고급 쿼리 기능과 관련하여서는 Querydsl의 공식 문서 또는 커뮤니티에서 제공하는 다양한 사례를 참고하시는 것도 좋은 방법입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

주우민님의 프로필

주우민

질문자

2024.05.24

제 질문이 해결이 안된것 같습니다. 지식공유자님 답변 해주시면 감사하겠습니다

채널톡 아이콘