2009-10-29 10 views
8

Estoy escribiendo un criterio NHibernate que selecciona los datos que admiten la búsqueda. Estoy usando la expresión COUNT(*) OVER() de SQL Server 2005 (+) para obtener la cantidad total de filas disponibles, como suggested por Ayende Rahien. Necesito ese número para poder calcular cuántas páginas hay en total. La belleza de esta solución es que no necesito ejecutar una segunda consulta para obtener el recuento de filas.Agregar una proyección a un criterio NHibernate le impide realizar la selección de entidad predeterminada

Sin embargo, parece que no logro escribir un criterio de trabajo (Ayende solo proporciona una consulta HQL).

Aquí hay una consulta SQL que muestra lo que quiero y funciona muy bien. Tenga en cuenta que intencionalmente en la lógica de paginación real para centrarse en el problema:

SELECT Items.*, COUNT(*) OVER() AS rowcount 
FROM Items 

aquí está el HQL:

select 
    item, rowcount() 
from 
    Item item 

Tenga en cuenta que la función rowcount() se ha registrado en una costumbre NHibernate dialecto y resuelve COUNT(*) OVER() en SQL.

El requisito es que la consulta se exprese utilizando un criterio. Por desgracia, no sé cómo hacer las cosas bien:

var query = Session 
    .CreateCriteria<Item>("item") 
    .SetProjection(
     Projections.SqlFunction("rowcount", NHibernateUtil.Int32)); 

Siempre que añado una proyección, NHibernate no selecciona item (como lo haría sin una proyección), mientras que sólo el rowcount() Realmente necesito tanto. Además, parece que no puedo proyectar item como un todo, solo sus propiedades y realmente no quiero enumerarlas todas.

Espero que alguien tenga una solución para esto. Gracias de cualquier manera.

Respuesta

5

Creo que no es posible en Criteria, tiene algunos límites.

Se podía obtener los elementos de identificación y de carga en una consulta posterior:

var query = Session 
    .CreateCriteria<Item>("item") 
    .SetProjection(Projections.ProjectionList() 
     .Add(Projections.SqlFunction("rowcount", NHibernateUtil.Int32)) 
     .Add(Projections.Id())); 

Si no te gusta, usar HQL, se puede establecer el número máximo de resultados allí también:

IList<Item> result = Session 
    .CreateQuery("select item, rowcount() from item where ...") 
    .SetMaxResult(100) 
    .List<Item>(); 
0

Use CreateMultiCriteria.

Puede ejecutar 2 instrucciones simples con solo un acierto en el DB de esa manera.

+0

Usando CreateMultiCriteria resultaría en dos consultas SQL separadas a generar. Aunque se ejecutarán en un solo lote, aún así no sería tan eficiente como ejecutar solo una consulta. Deseo 'SELECT *, COUNT (*) OVER() AS rowcount FROM Items', no 'SELECT * FROM Items; SELECCIONAR COUNT (*) COMO rowcount FROM Items 'como el escenario CreateMultiCriteria me atraparía. –

0

Me pregunto por qué usar Criteria es un requisito. ¿No puedes usar session.CreateSQLQuery? Si realmente tiene que hacerlo en una consulta, me habría sugerido tirando hacia atrás los objetos artículo y el recuento, como:

select {item.*}, count(*) over() 
from Item {item} 

... esta manera que pueda volver objetos de elemento de consulta, junto con el contar. Si tiene un problema con el almacenamiento en caché de Hibernate, también puede configurar los espacios de consulta (cachés de entidad/tabla) asociados con una consulta nativa para que las entradas de caché de consulta obsoletas se borren automáticamente.

+0

Gracias por su sugerencia, pero realmente quiero hacerlo utilizando Criteria porque de esa manera puedo aplicar fácilmente la búsqueda a muchos de mis Criterios existentes simplemente expandiéndolos usando un método de extensión 'Lista (inicio, límite, totalRowCount)'. Además, el uso de consultas SQL (Servidor) literales haría que mi solución sea menos agnóstica de plataforma, mientras que la solución actual que usa un dialecto personalizado, probablemente sería mucho más fácil de transferir a un DBMS diferente. –

0

Si entiendo su pregunta correctamente, tengo una solución. Luché bastante con este mismo problema.

Permítanme describir rápidamente el problema que tuve, para asegurarme de que estamos en la misma página. Mi problema se redujo a paginación. Quiero mostrar 10 registros en la interfaz de usuario, pero también quiero saber el número total de registros que coinciden con los criterios de filtro. Quería lograr esto usando la API de criterios de NH, pero al agregar una proyección para el recuento de filas, mi consulta ya no funcionaba, y no obtenía ningún resultado (no recuerdo el error específico, pero suena como lo que está recibiendo).

Aquí está mi solución (copie & pegue desde mi código de producción actual). Tenga en cuenta que "SessionError" es el nombre de la entidad comercial para la que estoy recuperando los datos paginados, de acuerdo con el criterio de 3 filtros: IsDev, IsRead y IsResolved.

ICriteria crit = CurrentSession.CreateCriteria(typeof (SessionError)) 
    .Add(Restrictions.Eq("WebApp", this)); 

if (isDev.HasValue) 
    crit.Add(Restrictions.Eq("IsDev", isDev.Value)); 

if (isRead.HasValue) 
    crit.Add(Restrictions.Eq("IsRead", isRead.Value)); 

if (isResolved.HasValue) 
    crit.Add(Restrictions.Eq("IsResolved", isResolved.Value)); 

// Order by most recent 
crit.AddOrder(Order.Desc("DateCreated")); 

// Copy the ICriteria query to get a row count as well 
ICriteria critCount = CriteriaTransformer.Clone(crit) 
    .SetProjection(Projections.RowCountInt64()); 
critCount.Orders.Clear(); 

// NOW add the paging vars to the original query 
crit = crit 
    .SetMaxResults(pageSize) 
    .SetFirstResult(pageNum_oneBased * pageSize); 

// Set up a multi criteria to get your data in a single trip to the database 
IMultiCriteria multCrit = CurrentSession.CreateMultiCriteria() 
    .Add(crit) 
    .Add(critCount); 

// Get the results 
IList results = multCrit.List(); 

List<SessionError> sessionErrors = new List<SessionError>(); 
foreach (SessionError sessErr in ((IList)results[0])) 
    sessionErrors.Add(sessErr); 

numResults = (long)((IList)results[1])[0]; 

Así que creo mis criterios base, con restricciones opcionales. Luego lo CLONE y agrego una proyección de recuento de filas a los criterios de CLONED. Tenga en cuenta que lo cloné antes de Agregué las restricciones de paginación. Luego configuré un IMultiCriteria para contener los objetos ICriteria originales y clonados, y uso el IMultiCriteria para ejecutarlos. Ahora tengo mis datos paginados del ICriteria original (y solo arrastré los datos que necesito a través del cable), y también un conteo sin procesar de cuántos registros reales coinciden con mis criterios (útiles para mostrar o crear enlaces de paginación, o lo que sea). Esta estrategia me ha funcionado bien. Espero que esto sea útil.

+0

Tenga en cuenta que no estoy utilizando una proyección RowCount porque eso solo contaría las filas realmente seleccionadas y no estoy interesado en eso. COUNT (*) OVER cuenta todas las filas, sin embargo, mitigando la necesidad de recuperarlo por separado. Aún así, aunque están agrupados, estás ejecutando dos consultas, lo que por supuesto es potencialmente menos eficiente que ejecutar solo una. Su solución está bien por sí sola, pero no cumple con mi requisito de ejecutar solo una consulta SQL. OK, no es un requisito real, pero no quiero renunciar a la oportunidad de optimizar aquí. –

0

Sugeriría investigar un transformador de resultados personalizado llamando a SetResultTransformer() en su sesión.

0

crear una propiedad de fórmula en el mapeo de clases:

<property name="TotalRecords" formula="count(*) over()" type="Int32" not-null="true"/>; 

IList<...> result = criteria.SetFirstResult(skip).SetMaxResults(take).List<...>(); 
totalRecords = (result != null && result.Count > 0) ? result[0].TotalRecords : 0; 
return result; 
+0

El total de registros no es propiedad de la entidad. Agregar este mapeo probablemente resultaría en problemas de caché. Ver también: http://stackoverflow.com/questions/1627707 – iammichael

+0

¡Gracias por el aviso! En mi caso, no tenía el caché de consultas habilitado y esto se proporcionó a través de una capa de servicio web sin estado, por lo que nunca se compartió la sesión. –

Cuestiones relacionadas