我正在使用 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 的列表,但它们作为单独的条目返回。
有没有办法构建此查询以正确聚合各自文章下的评论和反应?或者应该在获取数据后以编程方式处理?
任何建议或替代方法将不胜感激!
我实现的解决方案涉及使用 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() 对于正确聚合每篇文章下的评论和反应至关重要。 这种方法有效解决了聚合各自文章下的评论和反应的问题,同时确保了高效的数据获取和分页。
我希望这个详细的解释可以帮助其他面临类似情况的人。随时欢迎反馈或进一步优化的建议!