【问题标题】:QueryDSL: querying relations and propertiesQueryDSL:查询关系和属性
【发布时间】:2011-09-13 06:46:09
【问题描述】:

我将 QueryDSL 与 JPA 一起使用。

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

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

效果很好。

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

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 对惰性关系所做的那样),然后将结果加载进去?

还是应该只查询两次,然后合并两个结果列表?

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

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

并生成像(select group_concat(c.id) from comments as c inner join post where c.id = post.id)这样的子查询sql

【问题讨论】:

    标签: jpa querydsl


    【解决方案1】:

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

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

    【讨论】:

    • 感谢您的回答。不过我不太明白......在SQL方法中,你的意思是我可以在QueryDSL SQL中调用子查询吗?在后一种情况下,你的意思是我应该有一个 List commentIds 的单独属性(上面有一个公式)?
    • 您可以将子查询与 Querydsl SQL 一起使用,是的。关于你的第二个问题,不,只是普通属性,但延迟获取,使用带注释的属性,您可以访问 id 属性,但无需获取整个对象。
    • 谢谢!我之前不知道访问器注释的效果。我会调查的:)
    【解决方案2】:

    最简单的方法是查询 Posts 并将 fetchJoin 用于 cmets,但我假设这对您的用例来说太慢了。

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

    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.cmets 上优化集合获取。这样,您可以只查询 Posts,然后访问他们的 cmets 的 id,而几乎不会影响性能。在大多数情况下,除非您的评论非常胖,否则您甚至不需要那些懒惰的代理。如果没有低级别的行处理,这种代码肯定会更好看,您还可以在查询中使用限制和偏移量。只需密切关注您的查询日志,以确保一切正常。

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

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

    【讨论】:

    • 第一个解决方案看起来非常类似于 Playframework 的 Anorm 解析器 :) 你得到感兴趣的列,然后解析/提取/组合/展平结果。我真的希望 Querydsl 有这个开箱即用的功能......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 2020-09-28
    • 2017-03-15
    • 2015-12-05
    • 2019-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多