QueryDSL:查询关系和属性

发布于 2024-12-04 04:38:46 字数 967 浏览 2 评论 0原文

我将 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)

I'm using QueryDSL with JPA.

I want to query some properties of an entity, it's like this:

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

It works fine.

If i want to query a relation property, e.g. comments of a post:

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

It's also fine.

But when I want to query relation and simple properties together, e.g.

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

Then something went wrong, generiting a bad SQL syntax.

Then I realized that it's not possible to query them together in one SQL statement.

Is it possible that QueryDSL would somehow deal with relations and generate additional queries (just like what hibernate does with lazy relations), and load the results in?

Or should I just query twice, and then merge both result lists?

P.S. what i actually want is each post with its comments' ids. So a function to concat each post's comment ids is better, is this kind of expressin possible?

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

and generate a subquery sql like (select group_concat(c.id) from comments as c inner join post where c.id = post.id)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

傾城如夢未必闌珊 2024-12-11 04:38:46

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

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

Querydsl JPA is restricted to the expressivity of JPQL, so what you are asking for is not possible with Querydsl JPA. You can though try to express it with Querydsl SQL. It should be possible. Also as you don't project entities, but literals and collections it might work just fine.

Alternatively you can load the Posts with only the Comment ids loaded and then project the id, name and comment ids to something else. This should work when accessors are annotated.

橘虞初梦 2024-12-11 04:38:46

最简单的事情是查询帖子并使用 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));
}

注意:您不能对此类查询使用 limit 或 offset 。

作为替代方案,可以调整映射,以便 1) 注释始终是惰性代理,以便(通过属性访问)Comment.getId() 可以在不初始化实际对象的情况下实现;2) 在 Post 上使用批量 fetch*。评论以优化集合获取。这样,您只需查询帖子,然后访问其评论的 ID,对性能影响很小。在大多数情况下,您甚至不需要那些懒惰的代理,除非您的评论非常丰富。如果没有低级行处理,这种代码肯定看起来会更好,并且您还可以在查询中使用 limit 和 offset 。只需留意您的查询日志,以确保一切按预期运行。

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

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

The simplest thing would be to query for Posts and use fetchJoin for comments, but I'm assuming that's too slow for you use case.

I think you ought to simply project required properties of posts and comments and group the results by hand (if required). E.g.

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));
}

NOTE: You cannot use limit nor offset with this kind of queries.

As an alternative, it might be possible to tune your mappings so that 1) Comments are always lazy proxies so that (with property access) Comment.getId() is possible without initializing the actual object and 2) using batch fetch* on Post.comments to optimize collection fetching. This way you could just query for Posts and then access id's of their comments with little performance hit. In most cases you shouldn't even need those lazy proxies unless your Comment is very fat. That kind of code would certainly look nicer without low level row handling and you could also use limit and offset in your queries. Just keep an eye on your query log to make sure everything works as intended.

*) Batch fetching isn't directly supported by JPA, but Hibernate supports it through mapping and Eclipselink through query hints.

Maybe some day Querydsl will support this kind of results grouping post processing out-of-box...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文