Existe uma maneira de obter o tamanho da contagem de uma JPA chamada Query com um conjunto de resultados?

StackOverflow https://stackoverflow.com/questions/2285270

  •  21-09-2019
  •  | 
  •  

Pergunta

Gosto da idéia de consultas nomeadas no JPA para consultas estáticas que vou fazer, mas muitas vezes quero obter o resultado da contagem da consulta e da lista de resultados de algum subconjunto da consulta. Prefiro não escrever duas parques de nome quase idênticos. Idealmente, o que eu gostaria de ter é algo como:

@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();

Então, digamos que M seja 10, S 0 e há 400 linhas em conta. Eu esperaria que R tivesse uma lista de 10 itens, mas eu gostaria de saber que há 400 linhas no total. Eu poderia escrever um segundo @NamedQuery:

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

Mas parece uma violação seca fazer isso se eu sempre vou querer a contagem. Nesse caso simples, é fácil manter os dois em sincronia, mas se a consulta mudar, parece menos do que o ideal que eu precise atualizar as duas @NamedQueries para manter os valores alinhados.

Um caso de uso comum aqui seria buscar algum subconjunto dos itens, mas precisando de uma maneira de indicar a contagem total ("exibindo 1-10 de 400").

Foi útil?

Solução

Portanto, a solução que acabei usando foi criar dois @NamedQuerys, um para o conjunto de resultados e outro para a contagem, mas capturando a consulta base em uma corda estática para manter seca e garantir que ambas as consultas permaneçam consistentes. Então, para o exposto, eu teria algo como:

@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();

Obviamente, com este exemplo, o corpo da consulta é trivial e isso é um exagero. Mas com consultas muito mais complexas, você acaba com uma única definição do corpo da consulta e pode garantir que você tenha as duas consultas sincronizadas. Você também obtém a vantagem de que as consultas sejam pré -compiladas e, pelo menos com o Eclipselink, você obtém validação no horário de inicialização, em vez de quando liga para a consulta.

Ao fazer uma nomeação consistente entre as duas consultas, é possível envolver o corpo do código para executar os dois conjuntos apenas baseando o nome base da consulta.

Outras dicas

Usando setFirstResult/setMaxResults Faz não Retorne um subconjunto de um conjunto de resultados, a consulta nem foi executada quando você chama esses métodos, eles afetam a consulta selecionada gerada que será executada ao ligar getResultList. Se você quiser obter o total de registros, você terá que SELECT COUNT Suas entidades em uma consulta separada (normalmente antes de pagar).

Para um exemplo completo, confira Paginação de conjuntos de dados em um aplicativo de amostra usando JSF, Facade de Catalog Fatéssusfi.

Oh, bem, você pode usar a introspecção para obter anotações de consultas nomeadas como:

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top