使用 JPA 和 QueryDSL 聚合查询结果中的子实体

问题描述 投票:0回答:1

我正在使用 JPA 和 QueryDSL 开发 Java 应用程序,面临一对多关系查询的挑战。我有三个实体:文章、评论和反应。每篇文章(一篇)可以有多个评论和反应(很多)。我需要获取每篇文章及其汇总的评论和反应。

这是我当前的方法:

    public Page<ArticleDetail> findArticles(PageRequest pageRequest, User currentUser) {
    var articles = new JPAQueryFactory(entityManager)
            .select(Projections.constructor(ArticleDetail.class,
                    article.id,
                    Projections.constructor(UserDetail.class,
                            user.id,
                            user.name,
                            user.username,
                            user.email,
                            user.profilePicture,
                            user.level,
                            user.position),
                    article.content,
                    article.type,
                    Projections.list(Projections.constructor(CommentDetail.class,
                            comment.user.id,
                            comment.article.id,
                            comment.text,
                            comment.timestamp).skipNulls()).skipNulls(),
                    Projections.list(Projections.constructor(ReactionDetail.class,
                            reaction.user.id,
                            reaction.type).skipNulls()).skipNulls(),
                    article.commentCount,
                    article.dateCreated,
                    article.dateLastModified
                ))
                .from(article)
                .innerJoin(article.user, user)
                .leftJoin(article.comments, comment).on(comment.isActive.isTrue())
                .leftJoin(article.reactions, reaction)
                .where(article.isActive.isTrue(),
                        user.status.eq(Status.ACTIVE),
                        article.user.in(currentUser.getFollowing())
                                .or(article.user.eq(currentUser)))
                .offset(pageRequest.getOffset())
                .limit(pageRequest.getPageSize())
                .orderBy(article.id.asc())
                .fetch();

    return new PageImpl<>(articles, pageRequest, articles.size());
} 

实体:

@Entity
public class Article {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @OneToMany(mappedBy = "article")
    private Set<Comment> comments;

    @OneToMany(mappedBy = "article")
    private Set<Reaction> reactions;

    // Other fields like content, type, etc.
}

@Entity
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "article_id")
    private Article article;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    // Other fields like text, timestamp, etc.
}

@Entity
public class Reaction {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "article_id")
    private Article article;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    // Other fields like type, etc.
}

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(mappedBy = "user")
    private Set<Article> articles;

    // Other user fields like name, username, email, etc.
}

此方法应该返回一个 ArticleDetail 对象页面,每个对象包含文章的详细信息、作者、评论和反应。但是,我面临的问题是评论和反应没有正确汇总在各自的文章下。每个 ArticleDetail 实例应包含 CommentDetail 和 ReactionDetail 的列表,但它们作为单独的条目返回。

有没有办法构建此查询以正确聚合各自文章下的评论和反应?或者应该在获取数据后以编程方式处理?

任何建议或替代方法将不胜感激!

java sql jpa aggregation querydsl
1个回答
0
投票

我实现的解决方案涉及使用 QueryDSL 的两步查询过程:

首先,我获取了满足特定条件的文章的ID:

var articleIds = jpaQueryFactory
        .select(article.id)
        .from(article)
        // Conditions and joins
        .fetch();
var articles = jpaQueryFactory
        .select(article)
        .from(article)
        // Joins for comments and reactions
        .where(article.id.in(articleIds))
        .transform(groupBy(article.id).list(
            Projections.constructor(ArticleDetail.class,
                // Projection fields
            )));

return new PageImpl<>(articles, pageRequest, articles.size());

解决方案的关键方面: 利用两步查询过程首先获取文章 ID,然后通过聚合数据检索相应的文章。 使用 groupBy().list() 以及 Projections.constructor() 对于正确聚合每篇文章下的评论和反应至关重要。 这种方法有效解决了聚合各自文章下的评论和反应的问题,同时确保了高效的数据获取和分页。

我希望这个详细的解释可以帮助其他面临类似情况的人。随时欢迎反馈或进一步优化的建议!

© www.soinside.com 2019 - 2024. All rights reserved.