PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Dica HINT_PASS_DISTINCT_THROUGH reduz a quantidade de Entities retornadas por página para um PageRequest abaixo do tamanho da página configurada (PostgreSQL)


O problema que você está experimentando tem a ver com a maneira como você está usando o HINT_PASS_DISTINCT_THROUGH dica.

Esta dica permite que você indique ao Hibernate que o DISTINCT palavra-chave não deve ser usada no SELECT declaração emitida contra o banco de dados.

Você está aproveitando esse fato para permitir que suas consultas sejam classificadas por um campo que não está incluído no DISTINCT lista de colunas.

Mas não é assim que essa dica deve ser usada.

Esta dica só deve ser usada quando você tiver certeza de que não haverá diferença entre aplicar ou não um DISTINCT palavra-chave para o SQL SELECT instrução, porque o SELECT já irá buscar todos os valores distintos per se . A ideia é melhorar o desempenho da consulta evitando o uso de um DISTINCT desnecessário declaração.

Isso geralmente acontece quando você usa o query.distinct método em suas consultas de critérios e você está join fetching relacionamentos infantis. Este ótimo artigo de @VladMihalcea explicam detalhadamente como a dica funciona.

Por outro lado, quando você usa paginação, ele definirá OFFSET e LIMIT - ou algo semelhante, dependendo do banco de dados subjacente - no SQL SELECT declaração emitida contra o banco de dados, limitando a um número máximo de resultados sua consulta.

Conforme indicado, se você usar o HINT_PASS_DISTINCT_THROUGH dica, o SELECT declaração não conterá o DISTINCT palavra-chave e, por causa de suas junções, poderia fornecer registros duplicados de sua entidade principal. Esses registros serão processados ​​pelo Hibernate para diferenciar duplicatas, pois você está usando query.distinct , e de fato removerá duplicatas, se necessário. Acho que esse é o motivo pelo qual você pode obter menos registros do que o solicitado em seu Pageable .

Se você remover a dica, como DISTINCT palavra-chave é passada na instrução SQL que é enviada para o banco de dados, na medida em que você projeta apenas informações da entidade principal, ela buscará todos os registros indicados por LIMIT e é por isso que lhe dará sempre o número de registros solicitados.

Você pode tentar fetch join suas entidades filhas (em vez de apenas join com eles). Isso eliminará o problema de não poder usar o campo pelo qual você precisa classificar nas colunas do DISTINCT palavra-chave e, além disso, você poderá aplicar, agora de forma legítima, a dica.

Mas se você fizer isso, terá outro problema:se você usar join fetch e paginação, para retornar as entidades principais e suas coleções, o Hibernate não aplicará mais paginação no nível do banco de dados - não incluirá OFFSET ou LIMIT palavras-chave na instrução SQL e tentará paginar os resultados na memória. Este é o famoso Hibernate HHH000104 aviso:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!

@VladMihalcea explica isso detalhadamente na última parte de este artigo.

Ele também propôs uma possível solução para o seu problema, Window Functions .

No seu caso de uso, em vez de usar Specification s, a ideia é que você implemente seu próprio DAO. Este DAO só precisa ter acesso ao EntityManager , o que não é muito importante, pois você pode injetar seu @PersistenceContext :
@PersistenceContext
protected EntityManager em;

Depois de ter este EntityManager , você pode criar consultas nativas e usar funções de janela para construir, com base no Pageable fornecido informações, a instrução SQL correta que será emitida no banco de dados. Isso lhe dará muito mais liberdade sobre quais campos usam para classificação ou o que você precisar.

Como indica o último artigo citado, o Window Functions é um recurso suportado por todos os bancos de dados do prefeito.

No caso do PostgreSQL, você pode encontrá-los facilmente na documentação oficial .

Finalmente, mais uma opção, sugerida de fato por @nickshoe, e explicada detalhadamente no artigo ele citou, é realizar o processo de ordenação e paginação em duas fases:na primeira fase, você precisa criar uma consulta que fará referência às suas entidades filhas e na qual você aplicará paginação e ordenação. Esta consulta permitirá identificar os ids das principais entidades que serão utilizadas, na segunda fase do processo, para obter as próprias entidades principais.

Você pode aproveitar o DAO personalizado mencionado acima para realizar esse processo.