JPA

Querydsl: OneToMany 관계에서 Projection DTO 값 List를 어떻게 갖고올까?

초보병일이 2023. 4. 1. 12:04
728x90

이 코드의 목적

제목, 글쓴이, 제목 + 내용 등을 통한 게시글 검색 기능입니다.
게시글 리스트를 불러올 때 해시태그도 보여줘야 하기 때문에 List값이 필요합니다.

이 오류는 왜 발생했을까?

Entity입니다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post extends BaseEntity{

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
    private String thumbnail;
    @Column(length = 100, nullable = false)
    private String title;
    @Column(length = 100, nullable = false)
    private String content;
    private Long hits;
    private int report;

    @Enumerated(EnumType.STRING)
    private Category category;

    @OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE)
    private List<Comment> comment = new ArrayList<>();

    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    private List<PostLike> postLike = new ArrayList<>();

    @OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE)
    private List<HashTag> hashTag = new ArrayList<>();

HashTagEntity

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class HashTag {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "hashtag_id")
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "tag_id")
    private Tag tag;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    private Post post;

Post와 HashTag는 1:N 관계입니다.

두 개를 묶은 SearchDto가 필요합니다.
DTO입니다.

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SearchTestDto {

    private Long postId;
    private Long userId;
    private String nickname;
    private String thumbnail;
    private String title;
    private String content;
    private Long hits;
    private Category category;
    private Long commentCount;
    private Long likeCount;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Seoul")
    private LocalDateTime createAt;
    private List<HashTag> hashTag = new ArrayList<>();
}

Post를 조회하고, HashTag는 Left Join을 해야 합니다.
작성한 QueryDSL입니다.

public Page<SearchTestDto> searchPageSimpleV2(PostSearchCondition condition, Pageable pageable) {
    System.out.println("==========================================================");
    List<SearchTestDto> content = queryFactory
            .select(Projections.constructor(SearchTestDto.class,
                    post.id,
                    user.id,
                    user.nickname,
                    post.thumbnail,
                    post.title,
                    post.content,
                    post.hits,
                    post.category,
                    post.comment.size().longValue().as("commentCount"),
                    post.postLike.size().longValue().as("likeCount"),
                    post.createAt,
                    post.hashTag))
            .from(post)
            .leftJoin(post.user, user)
            .leftJoin(post.hashTag, hashTag)
            .where(
                    titleEq(condition.getTitle())
            )
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

이 코드를 작성한 후 실행하면?

2가지 이유로 실패합니다.

  1. List로 조회되는 HashTag를 제대로 인식하지 못합니다.
  2. InnerJoin, LeftJoin이 각각 적용되어 문제가 발생합니다.

이상한 . 컬럼, left inner join

그럼 이상황에선 어떻게 하면 될까요?

해결방법

Post Entity를 직접 조회 후 그 결과로 DTO를 생성하는 것입니다.

List<Post> posts = queryFactory
        .selectFrom(post)
        .join(post.user, user).fetchJoin()
        .where(
                titleEq(condition.getTitle())
        )
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize())
        .fetch();

List<SearchTestDto> content = posts.stream()
        .map(p -> new SearchTestDto(p, p.getComment().size(), p.getPostLike().size()))
        .collect(Collectors.toList());

문제 없이 완료!

 

728x90