java - una - Hibernar Seleccionar las filas superiores e inferiores con criterios
seleccionar un rango de celdas en excel macro (7)
Digamos que tengo dos tablas, libros y reseñas. Las revisiones tienen una columna, estrellas, que pueden tener un valor entre 1 y 5. Un libro puede tener muchas revisiones.
¿Cómo seleccionaría todos los libros de modo que solo se devuelvan las 3 revisiones superior e inferior para cada libro (en lugar de todas las revisiones) utilizando el Criteria API?
Si la API de criterios no es capaz, estoy preparado para otras sugerencias como HQL, SQL, etc.
¿Qué tal el mapeo para la Revisión y el Libro como muchos a muchos, y luego en la colección de reseñas del Libro, especifique: order-by = "rating desc", y lazy = "true"
Supuestamente con el método de recuperación perezosa, los datos se buscarán solo cuando el objeto se obtenga de la colección, pero no estoy seguro de esto. Para confirmar, puede activar el registro de Hibernate SQL y controlar qué consultas se han realizado durante el tiempo de ejecución.
Dos consultas HQL usando org.springframework.data.domain.Pageable como:
antes, en el servicio de capa media, creamos un servicio de paginación y de llamadas:
Pageable pageableTop = new PageRequest(0, 3, Direction.ASC, "yourFieldToSortBy");
yourRepository.findTopOrderByYourEntity(pageableTop);
Pageable pageableBottom = new PageRequest(0, 3, Direction.DESC, "yourFieldToSortBy");
yourRepository.findTopOrderByYourEntity(pageableBottom);
la repo
public interface YourRepository extends CrudRepository<YourEntity, String> {
@Query("FROM YourEntity")
List<YourEntity> findTopOrderByYourEntity(Pageable pageable);
}
Esto normalmente se haría con algo como una unión, que no se puede hacer en HQL.
Puedes hacer esto, sin embargo, en HQL
WHERE id in ([SELECT top 3]) or id in ([SELECT bottom 3])
No tengo una manera de probar esto, pero esto también podría funcionar.
DetachedCriteria topN = [ your criteria ]
DetachedCriteria bottomN = [ your criteria ]
session.createCriteria(..._
.add( Property.or(Property.forName("id").in(topN),Property.forName("id").in(bottomN) )
.list();
No creo que puedas hacer lo que quieres con una sola consulta. Sin embargo, puede hacerlo en dos (usando EJBQL con parámetros nombrados):
SELECT r FROM Reviews r WHERE r.book = :book ORDER BY r.stars ASC LIMIT 3;
SELECT r FROM Reviews r WHERE r.book = :book ORDER BY r.stars DESC LIMIT 3;
Y, por supuesto, podría escribir un método simple de ayuda que realice ambas llamadas y compile los resultados en la estructura de datos que prefiera.
Prueba esto y hazme saber si funciona para ti:
// you should have the DAO class containing this queries
// extend HibernateDaoSupport
List<Tag> topList = getSession().createQuery("FROM Reviews r WHERE r.book = "
+ book + " ORDER BY r.stars asc").setMaxResults(3).list();
List<Tag> bottomList = getSession().createQuery("FROM Reviews r WHERE r.book = "
+ book + " ORDER BY r.stars desc").setMaxResults(3).list();
Respondiendo a mi propia pregunta ...
Estoy realmente sorprendido de lo difícil que es esto. Todas las soluciones que involucran la consulta de Libros y cada Libro que tiene su lista de Revisiones superior e inferior dan como resultado N consultas por Libro en algunas bases de datos, y para la mía en particular (MySQL) requiere 3N. Por supuesto, alguien puede ser capaz de descubrir una forma inteligente de solucionar esto, pero mi juego con el código, el SQL sin procesar, la investigación de lo que otros han hecho, las limitaciones de la base de datos, etc. parece que siempre se remonta a eso.
Entonces ... terminé cambiando el problema. En lugar de calcular las Revisiones superior e inferior siempre que solicito Libros (que es una consulta muy frecuente, los Libros / Revisiones FYI son, por ejemplo, el dominio real es diferente), calculo la parte superior / inferior cada vez que se envíe una Revisión. Esto cambia el envío de una Revisión de una "consulta" a tres consultas, pero enviar una Revisión es bastante infrecuente en comparación con la consulta de Libros.
Para hacer esto, agregué dos nuevas propiedades a Book, topReviews y bottomReviews y las asigné como listas de uno a varios en Review. Luego actualicé el código que guarda nuevas Revisiones para consultar las Revisiones superior e inferior del Libro en cuestión (2 consultas), establece las propiedades de las Revisiones superior / inferior en el Libro (sobrescribiendo las anteriores) y guarda el Libro. Las consultas de cualquier libro ahora devuelven estas revisiones superior e inferior "en caché". Todavía tengo una propiedad de "comentarios" perezosa que tendrá todos los comentarios (no solo arriba / abajo) si así lo desea: otro mapeo de uno a muchos.
Estoy abierto a otras recomendaciones. La mayoría de las respuestas aquí se encuentran en el parque de juegos, pero se saltan el problema o no funcionan debido a limitaciones de la base de datos o requieren demasiadas consultas.
Siempre se puede establecer el orden de asc / desc y luego limitar los resultados.
ex:
criteria.addOrder(Order.desc("id"));
criteria.setMaxResults(1);
No estoy seguro de dónde se encuentra con respecto a la eficiencia. ¿Asumo que está haciendo el filtrado después del hecho de que un Select *
ha sido disparado?