17

我喜欢 JPA 中的命名查询的想法,用于我将要执行的静态查询,但我经常想要获取查询的计数结果以及来自查询的某个子集的结果列表。我宁愿不写两个几乎相同的 NamedQueries。理想情况下,我想要的是:

@NamedQuery(name = "getAccounts", query = "SELECT a FROM Account")
.
.
  Query q = em.createNamedQuery("getAccounts");
  List r = q.setFirstResult(s).setMaxResults(m).getResultList();
  int count = q.getCount();

假设 m 为 10,s 为 0,Account 中有 400 行。我希望 r 中有一个包含 10 个项目的列表,但我想知道总共有 400 行。我可以写第二个@NamedQuery:

@NamedQuery(name = "getAccountCount", query = "SELECT COUNT(a) FROM Account")

但如果我总是想要计数,那么这样做似乎是违反 DRY 的。在这种简单的情况下,很容易使两者保持同步,但如果查询发生变化,我必须更新两个 @NamedQueries 以保持值一致,这似乎不太理想。

这里的一个常见用例是获取项目的一些子集,但需要某种方式来指示总计数(“显示 400 的 1-10”)。

4

3 回答 3

14

所以我最终使用的解决方案是创建两个@NamedQuery,一个用于结果集,一个用于计数,但在静态字符串中捕获基本查询以保持 DRY 并确保两个查询保持一致。因此,对于上述内容,我会有类似的东西:

@NamedQuery(name = "getAccounts", query = "SELECT a" + accountQuery)
@NamedQuery(name = "getAccounts.count", query = "SELECT COUNT(a)" + accountQuery)
.
static final String accountQuery = " FROM Account";
.
  Query q = em.createNamedQuery("getAccounts");
  List r = q.setFirstResult(s).setMaxResults(m).getResultList();
  int count = ((Long)em.createNamedQuery("getAccounts.count").getSingleResult()).intValue();

显然,在这个例子中,查询主体是微不足道的,这是多余的。但是对于更复杂的查询,您最终会得到一个查询主体的定义,并且可以确保两个查询同步。您还可以获得查询是预编译的优势,并且至少使用 Eclipselink,您可以在启动时而不是在调用查询时获得验证。

通过在两个查询之间进行一致的命名,只需基于查询的基本名称就可以包装代码主体以运行这两个集合。

于 2010-10-10T16:25:54.473 回答
6

使用setFirstResult/setMaxResults不返回结果集的子集,当您调用这些方法时查询甚至还没有运行,它们会影响调用时将执行的生成的 SELECT 查询getResultList。如果要获取总记录数,则必须SELECT COUNT在单独的查询中对实体进行查询(通常在分页之前)。

有关完整示例,请查看使用 JSF、Catalog Facade Stateless Session 和 Java Persistence API 在示例应用程序中对数据集进行分页

于 2010-02-17T23:49:28.700 回答
1

哦,您可以使用自省来获取命名查询注释,例如:

String getNamedQueryCode(Class<? extends Object> clazz, String namedQueryKey) {
    NamedQueries namedQueriesAnnotation = clazz.getAnnotation(NamedQueries.class);
    NamedQuery[] namedQueryAnnotations = namedQueriesAnnotation.value();

    String code = null;
    for (NamedQuery namedQuery : namedQueryAnnotations) {
        if (namedQuery.name().equals(namedQueryKey)) {
            code = namedQuery.query();
            break;
        }
    }

    if (code == null) {
        if (clazz.getSuperclass().getAnnotation(MappedSuperclass.class) != null) {
            code = getNamedQueryCode(clazz.getSuperclass(), namedQueryKey);
        }
    }

    //if not found
    return code;
}
于 2011-06-28T16:45:21.753 回答