Подтвердить что ты не робот

Есть ли способ получить размер счета для 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" ).

4b9b3361

Ответ 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;
}