Извлечение пагинации с коллекцией
Рассмотрим две сущности Person
который имеет one-to-many
коллекция Vehicles
public class Person
{
public IList<Vehicle> Vehicles { get; set;}
}
public class Vehicle
{
public string Name { get; set;}
public Person Owner { get; set; }
}
Я отображаю сетку лиц, имеющих транспортное средство, и показываю имя первого транспортного средства в сетке. Сетка разбита на страницы. Я использую следующие критерии для извлечения данных
У меня есть критерии для загрузки данных для вида сетки как
var criteria = DetachedCriteria.For<Person>()
.CreateAlias("Vehicles","vehicle", JoinType.InnerJoin)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.SetMaxResults(pageSize)
.SetFirstResult((page - 1) * pageSize)
criteria.Add(Restrictions.Eq("vehicle.Name", "super"));
где page
а также pageSize
рассчитываются биты.
Проблема в том, что максимальные результаты и первые результаты вычисляются в базе данных, а внешний корень выполняется снаружи, количество строк не совпадает.
Есть ли способ решить эту проблему?
1 ответ
Этот вид запросов должен всегда использовать subquery
вместо любого типа JOIN. Это также означает, что элемент коллекционирования имеет ссылку на родителя (как в нашем случае).
Итак, здесь мы создаем внутренний выбор для Vehicle
:
var vehicles = DetachedCriteria.For<Vehicle>();
// add any amount or kind of WHERE parts
vehicles.Add(Restrictions.Eq("vehicle.Name", "super"))
// and essential SELECT Person ID
vehicles.SetProjection( Projections.Property("Owner.ID"));
Теперь мы можем настроить вышеупомянутый запрос, чтобы он работал только на уровне root/parent:
var criteria = DetachedCriteria.For<Person>()
// instead of this
// .CreateAlias("Vehicles","vehicle", JoinType.InnerJoin)
// we will use subquery
.Add(Subqueries.PropertyIn("ID", vehicles));
// Wrong to use this approach at all
//.SetResultTransformer(new DistinctRootEntityResultTransformer())
.SetMaxResults(pageSize)
.SetFirstResult((page - 1) * pageSize)
Это создаст SELECT следующим образом:
SELECT p....
FROM Person AS p
WHERE p.ID IN (
SELECT v.OwnerId
FROM Vehcile AS v
WHERE v.Name = 'super' ...
)
Смотрите также:
И как получить коллекцию Vehicles
(до сих пор просто используется для фильтрации)? Лучший (если не единственный) способ - использовать операторы SELECT 1 + 1. Простое и встроенное решение batch-size
установка. Просто отметьте коллекцию Vehicles
с этой настройкой (например, batch-size="25"
) и с помощью еще нескольких операторов SELECT все данные будут эффективно загружены. Увидеть: