Есть ли способ получить размер счета для JPA Named Query с набором результатов?
Мне нравится идея Named Queries в 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" ).
Ответы
Ответ 1
Итак, решение, которое я закончил использовать, состояло в том, чтобы создать два @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, вы получаете подтверждение во время запуска, а не при вызове запроса.
Выполняя последовательное именование между двумя запросами, можно обернуть тело кода, чтобы запустить оба набора, просто основывая базовое имя запроса.
Ответ 2
Использование setFirstResult
/setMaxResults
do не возвращает подмножество набора результатов, запрос даже не запускался при вызове этих методов, они влияют на сгенерированный запрос SELECT, который будет выполняться при вызове getResultList
. Если вы хотите получить общее количество записей, вы должны будете SELECT COUNT
ваши объекты в отдельном запросе (как правило, перед тем, чтобы разбивать на страницы).
В качестве полного примера ознакомьтесь с Разбиение наборов данных в примерном приложении с использованием JSF, Session Facade Session Facade и API Persistence API.
Ответ 3
Хорошо, вы можете использовать интроспекцию для получения аннотаций с именованными запросами типа:
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;
}