有没有办法获得具有结果集的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 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; }