【问题标题】:JPA/MySQL - Whats wrong with this JPQL query?JPA/MySQL - 这个 JPQL 查询有什么问题?
【发布时间】:2014-01-09 01:35:54
【问题描述】:

我有实体Post:

@Entity
@Table(name = "post")
@NamedQueries({
    @NamedQuery(name = "getNewestPosts", query = "SELECT p FROM Post p ORDER BY p.date DESC"), // getting resultList ordered by date
    @NamedQuery(name = "getMostVisitedPosts", query = "SELECT p FROM Post p ORDER BY p.visitors DESC"), // ordered by most visited
    @NamedQuery(name = "getMostCommentedPosts", query = "SELECT p FROM Post p ORDER BY SIZE(p.comments) DESC")
})
public class Post implements Serializable {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "post_id", unique = true, nullable = false)
    private Integer id; 

    @Column(name = "post_title", length=300, unique = false, nullable = false)
    private String title;

    @Column(name = "post_date", unique = false, nullable = false)
    private Date date;

    @Column(name = "post_summary", length=1000, unique = false, nullable = true)
    private String summary;

    @Column(name = "post_content", length=50000, unique = false, nullable = false)
    private String content;

    @Column(name = "post_visitors", unique = false, nullable = false)
    private Integer visitors;

    @ManyToOne
    @JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
    private User user;

    @ManyToOne
    @JoinColumn (name = "category_id", referencedColumnName="category_id", nullable = false)
    private Category category;

    @OneToMany(cascade = { ALL }, fetch = EAGER, mappedBy = "post")
    private Set<Comment> comments = new HashSet<Comment>();
...

实体Comment

@Entity
@Table(name = "comment")
public class Comment implements Serializable {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "comment_id", unique = true, nullable = false)
    private Integer id; 

    @Column(name = "comment_title", length=300, unique = false, nullable = false)
    private String title;

    @Column(name = "comment_date", unique = false, nullable = false)
    private Date date;

    @Column(name = "comment_content", length=600, unique = false, nullable = false)
    private String content;

    @ManyToOne
    @JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
    private User user;

    @ManyToOne
    @JoinColumn (name = "post_id", referencedColumnName="post_id", nullable = false)
    private Post post;
...

那么,PostDAOBean 中的方法如下:

public List<Post> getMostCommentedPosts(int page, int postsPerPage){

    Query q = em.createNamedQuery("getMostCommentedPosts");
    q.setFirstResult(page - 1);
    q.setMaxResults(postsPerPage);
    List<Post> resultList = (List<Post>) q.getResultList();

    if (resultList.isEmpty())
        return null;
    else
        return resultList;
}

当我在 servlet 中调用此方法时,我得到下一个异常:

[07.01.2014 09:06] Class name: class mbs2.blog.server.session.PostDAOBean, method name: public java.util.List mbs2.blog.server.session.PostDAOBean.getMostCommentedPosts(int,int) started
[07.01.2014 09:06] Class name: class mbs2.blog.server.session.PostDAOBean, method name: public java.util.List mbs2.blog.server.session.PostDAOBean.getMostCommentedPosts(int,int)<openjpa-2.2.0-r422266:1244990 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: "Encountered "SIZE" at character 31, but expected: ["AVG", "COUNT", "KEY", "MAX", "MIN", "SUM", "VALUE", <IDENTIFIER>]." while parsing JPQL "SELECT p FROM Post p ORDER BY SIZE(p.comments) DESC". See nested stack trace for original parse error.
    at org.apache.openjpa.kernel.jpql.JPQLParser.parse(JPQLParser.java:51)
    at org.apache.openjpa.kernel.ExpressionStoreQuery.newCompilation(ExpressionStoreQuery.java:154)
    at org.apache.openjpa.kernel.QueryImpl.newCompilation(QueryImpl.java:672)
    at org.apache.openjpa.kernel.QueryImpl.compilationFromCache(QueryImpl.java:654)
    at org.apache.openjpa.kernel.QueryImpl.compileForCompilation(QueryImpl.java:620)
    at org.apache.openjpa.kernel.QueryImpl.compileForExecutor(QueryImpl.java:682)
    at org.apache.openjpa.kernel.QueryImpl.compile(QueryImpl.java:589)
    at org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:1038)
    at org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:102)
    at org.apache.openejb.persistence.JtaEntityManager.createNamedQuery(JtaEntityManager.java:274)
    at mbs2.blog.server.session.PostDAOBean.getMostCommentedPosts(PostDAOBean.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...

您可以看到命名查询是:SELECT p FROM Post p ORDER BY SIZE(p.comments) DESC

异常的原因是什么?

编辑 好的,我看到函数 SIZE(p.comments) 不能在 JPQL 中使用以由它订购。

使用 JPQL 获取相同数据的替代方法是什么?正确的 JPQL 查询?

编辑 尝试使用 JPA Criteria API 实现此查询:

    public List<Post> getMostCommentedPosts(int page, int postsPerPage){

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Post> cq = cb.createQuery(Post.class);
        Root<Post> p = cq.from(Post.class);
        cq.select(p);
        cq.orderBy(cb.desc(???));
                ...???...
}

在 JPA Criteria API 方面需要帮助。

【问题讨论】:

  • 我不认为您可以在 SQL 中将聚合放在 ORDER BY 中。不过可能是错的。
  • @CodeChimp hm,其他两个命名查询也使用ORDER BY,并且它们对应的方法有效
  • 看到异常,据说 jpa 支持 ["AVG", "COUNT", "KEY", "MAX", "MIN", "SUM", "VALUE", ]。而你使用 SIZE 函数
  • 啊,我明白了问题...没有 SIZE 聚合函数。它在错误文本中。
  • @CodeChimp 我应该如何修改 JPQL 查询以获取按其 cmets 数量排序的所有帖子?感谢任何帮助

标签: java mysql jsp jpa jpql


【解决方案1】:

好吧,编译器的叫声:“在第 31 个字符处遇到“SIZE”,但预期:[“AVG”、“COUNT”、“KEY”、“MAX”、“MIN”、“SUM”、“VALUE”、] 。”在解析 JPQL 时...."

SIZE 是一个不符合 ORDER BY 子句规范的函数:

ORDER BY 子句允许对查询返回的对象或值进行排序。 ORDER BY 子句的语法是 orderby_clause ::= ORDER BY orderby_item {, orderby_item}* orderby_item ::= state_field_path_expression [ASC |描述] 用 MAX 或 MIN 指定 DISTINCT 是合法的,但不影响结果。 在查询中使用 ORDER BY 子句时,查询的 SELECT 子句的每个元素必须是以下之一:标识变量 x,可选地表示为 OBJECT(x)、single_valued_association_path_expression 或 state_field_path_expression。

进一步:

SIZE 函数返回一个整数值,即集合元素的数量。如果集合为空,则 SIZE 函数的计算结果为零。


试试这个:

public List<Post> getMostCommentedPosts(int page, int postsPerPage){

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Post> cq = cb.createQuery(Post.class);
        Root<Post> p = cq.from(Post.class);
        cq.select(p).where(cb.isNotEmpty(p.get("comments")));
        List list = em.createQuery(cq).getResultList();
        for(Set each : list)
        {
            System.out.println(each.size());
        }

}

【讨论】:

  • 是的,我现在明白了,谢谢。你能帮我吗:我可以用什么来代替 SIZE(p.comments) 来获取集合中的 cmets 数量?
  • 您可以获得 SIZE(p.cmets),只是无法订购。 SIZE() 必须在 WHERE 或 HAVING 子句中指定。 SELECT p FROM Post p WHERE SIZE(p.cmets) > 0 之类的东西。看起来集合对象本身没有聚合,因此您可能必须在 Set 本身上执行此操作。我会再调查一下。
  • 查看此页面。看起来您可以将 size 与 JPA CriteriaBuilder 一起使用。 objectdb.com/java/jpa/query/jpql/…
  • JPA Criteria API 看起来很混乱。你熟悉吗?如果您知道如何完成该方法,请参阅我的答案编辑?
猜你喜欢
  • 2011-08-04
  • 2017-03-07
  • 1970-01-01
  • 2011-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多