El problema que está experimentando tiene que ver con la forma en que está usando el HINT_PASS_DISTINCT_THROUGH
pista.
Esta sugerencia le permite indicar a Hibernate que DISTINCT
la palabra clave no debe usarse en SELECT
declaración emitida contra la base de datos.
Está aprovechando este hecho para permitir que sus consultas se ordenen por un campo que no está incluido en DISTINCT
lista de columnas.
Pero no es así como se debe usar esta pista.
Esta sugerencia solo debe usarse cuando esté seguro de que no habrá diferencia entre aplicar o no un DISTINCT
palabra clave al SQL SELECT
declaración, porque el SELECT
declaración ya obtendrá todos los valores distintos per se . La idea es mejorar el rendimiento de la consulta evitando el uso de un DISTINCT
innecesario declaración.
Esto es generalmente lo que sucederá cuando use el query.distinct
método en sus consultas de criterios, y está join fetching
relaciones infantiles. Este gran artículo
de @VladMihalcea explica en detalle cómo funciona la pista.
Por otro lado, cuando usa paginación, establecerá OFFSET
y LIMIT
- o algo similar, dependiendo de la base de datos subyacente - en SQL SELECT
comunicado emitido contra la base de datos, limitando a un número máximo de resultados su consulta.
Como se indicó, si usa el HINT_PASS_DISTINCT_THROUGH
pista, el SELECT
declaración no contendrá el DISTINCT
palabra clave y, debido a sus uniones, podría generar registros duplicados de su entidad principal. Hibernate procesará estos registros para diferenciar los duplicados, porque está utilizando query.distinct
, y de hecho eliminará los duplicados si es necesario. Creo que esta es la razón por la que puede obtener menos registros de los solicitados en su Pageable
.
Si elimina la sugerencia, como DISTINCT
La palabra clave se pasa en la declaración SQL que se envía a la base de datos, en la medida en que solo proyecte información de la entidad principal, obtendrá todos los registros indicados por LIMIT
y por eso te dará siempre el número de registros solicitado.
Puedes probar y fetch join
sus entidades secundarias (en lugar de solo join
con ellos). Eliminará el problema de no poder usar el campo por el que necesita ordenar en las columnas de DISTINCT
palabra clave y, además, podrás aplicar, ahora legítimamente, la indirecta.
Pero si lo hace, tendrá otro problema:si usa búsqueda conjunta y paginación, para devolver las entidades principales y sus colecciones, Hibernate ya no aplicará la paginación a nivel de base de datos; no incluirá OFFSET
o LIMIT
palabras clave en la instrucción SQL e intentará paginar los resultados en la memoria. Este es el famoso Hibernate HHH000104
advertencia:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalcea explica eso con gran detalle en la última parte de esto artículo.
También propuso una posible solución a su problema, Funciones de ventana .
En su caso de uso, en lugar de usar Specification
s, la idea es que implementes tu propio DAO. Este DAO solo necesita tener acceso al EntityManager
, lo cual no es gran cosa ya que puedes inyectar tu @PersistenceContext
:
@PersistenceContext
protected EntityManager em;
Una vez que tenga este EntityManager
, puede crear consultas nativas y usar funciones de ventana para compilar, según el Pageable
proporcionado. información, la instrucción SQL correcta que se emitirá contra la base de datos. Esto le dará mucha más libertad sobre qué campos usar para ordenar o lo que necesite.
Como indica el último artículo citado, las funciones de ventana son una función compatible con todas las principales bases de datos.
En el caso de PostgreSQL, puede encontrarlos fácilmente en la documentación oficial .
Finalmente, una opción más, sugerida de hecho por @nickshoe, y explicada con gran detalle en el artículo citó, es realizar el proceso de ordenación y paginación en dos fases:en la primera fase, debe crear una consulta que haga referencia a sus entidades secundarias y en la que aplicará la paginación y la ordenación. Esta consulta le permitirá identificar los id de las principales entidades que se utilizarán, en la segunda fase del proceso, para obtener las principales entidades.
Puede aprovechar el DAO personalizado mencionado anteriormente para realizar este proceso.