2

我将 QueryDSL 与 JPA 一起使用。

我想查询一个实体的一些属性,是这样的:

QPost post = QPost.post;
JPAQuery q = new JPAQuery(em);
List<Object[]> rows = q.from(post).where(...).list(post.id, post.name);

它工作正常。

如果我想查询关系属性,例如帖子的评论:

List<Set<Comment>> rows = q.from(post).where(...).list(post.comments);

这也很好。

但是当我想一起查询关系和简单属性时,例如

List<Object[]> rows = q.from(post).where(...).list(post.id, post.name, post.comments);

然后出现了问题,产生了错误的 SQL 语法。

然后我意识到不可能在一个 SQL 语句中同时查询它们。

QueryDSL 是否有可能以某种方式处理关系并生成额外的查询(就像 hibernate 对惰性关系所做的那样)并加载结果?

或者我应该只查询两次,然后合并两个结果列表?

PS我真正想要的是每个帖子都有评论的ID。所以连接每个帖子的评论ID的功能更好,这种表达可能吗?

q.list(post.id, post.name, post.comments.all().id.join())

并生成一个子查询sql(select group_concat(c.id) from comments as c inner join post where c.id = post.id)

4

2 回答 2

5

Querydsl JPA 仅限于 JPQL 的表达能力,因此您所要求的对于 Querydsl JPA 是不可能的。您可以尝试使用 Querydsl SQL 来表达它。这应该是可能的。此外,由于您不投影实体,但文字和集合可能工作得很好。

或者,您可以加载仅加载评论 ID 的帖子,然后将 ID、名称和评论 ID 投影到其他内容。这应该在访问器被注释时起作用。

于 2011-09-13T07:18:19.050 回答
3

最简单的方法是查询 Posts 并使用 fetchJoin 进行评论,但我假设这对您的用例来说太慢了。

我认为您应该简单地投影帖子和评论的所需属性,并手动对结果进行分组(如果需要)。例如

QPost post=...;
QComment comment=..;

List<Tuple> rows = q.from(post)
// Or leftJoin if you want also posts without comments
.innerJoin(comment).on(comment.postId.eq(post.id))
.orderBy(post.id) // Could be used to optimize grouping
.list(new QTuple(post.id, post.name, comment.id));

Map<Long, PostWithComments> results=...;
for (Tuple row : rows) {
  PostWithComments res = results.get(row.get(post.id));
  if (res == null) { 
    res = new PostWithComments(row.get(post.id), row.get(post.name));
    results.put(res.getPostId(), res);
  }
  res.addCommentId(row.get(comment.id));
}

注意:您不能对此类查询使用限制或偏移。

作为替代方案,可以调整您的映射,以便 1)评论始终是惰性代理,以便(具有属性访问)Comment.getId() 无需初始化实际对象和 2)在 Post 上使用批量获取*。评论以优化集合获取。这样,您可以只查询帖子,然后访问他们评论的 id,而几乎不会影响性能。在大多数情况下,除非您的评论非常胖,否则您甚至不需要那些懒惰的代理。如果没有低级别的行处理,这种代码肯定会更好看,您还可以在查询中使用限制和偏移量。只需密切注意您的查询日志,以确保一切按预期工作。

*) JPA 不直接支持批量获取,但 Hibernate 通过映射和 Eclipselink 通过查询提示支持它。

也许有一天Querydsl会支持这种开箱即用的结果分组后处理......

于 2011-09-13T08:43:36.373 回答