有没有办法获得具有结果集的JPA命名查询的计数大小?

我喜欢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") 

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

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

所以我最终使用的解决方案是创建两个@NamedQuerys,一个用于结果集,一个用于计数,但是在静态字符串中捕获基本查询以保持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,您可以在启动时进行validation,而不是在调用查询时进行validation。

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

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

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

哦,你可以使用内省来获得命名查询注释,如:

 String getNamedQueryCode(Class 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; }