Допустим, у нас есть таблица категорий и таблица продуктов. И каждый продукт ссылается на категорию. Таким образом, в каждой категории есть много продуктов. Я хочу загрузить много категорий без продуктов (чтобы уменьшить время доступа к БД), затем проверить, какие категории нам действительно нужны, и определить гораздо меньшее подмножество категорий. После этого мне нужно загрузить все продукты для выбранных категорий и прикрепить их к категориям с помощью одного запроса к БД. Я могу загружать товары отдельно, но в этом случае они не будут привязаны к категориям.
Массовая загрузка дочерних объектов NHibernate для нескольких родителей
Ответы (2)
Этого можно достичь с помощью HQL и фьючерсов.
учитывая сущности и карты следующим образом,
public class Category
{
private IList<Product> _products;
public Category()
{
_products = new List<Product>();
}
public virtual int Id { get; set; }
public virtual string CategoryName { get; set; }
public virtual IList<Product> Products
{
get { return _products; }
set { _products = value; }
}
}
public class CategoriesClassMap : ClassMap<Category>
{
public CategoriesClassMap()
{
Table("Categories");
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.CategoryName);
HasMany<Product>(c => c.Products).LazyLoad();
}
}
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductName { get; set; }
public virtual Category Category { get; set; }
}
public class ProductSClassMap : ClassMap<Product>
{
public ProductSClassMap()
{
Table("Products");
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.ProductName);
References<Category>(x => x.Category).Not.Nullable();
}
}
При следующем HQL он загрузит все категории и продукты в одном запросе,
var categories = session.CreateQuery("from Category c join fetch c.Products where c.Id in (1,2)")
.Future<Category>().Distinct().ToList();
Он извлекает только данные, относящиеся к идентификаторам категорий 1 и 2. Сгенерированный SQL выглядит так:
select category0_.Id as Id1_0_, products1_.Id as Id3_1_, category0_.CategoryName as Category2_1_0_, products1_.ProductName as ProductN2_3_1_, products1_.Category_id as Category3_3_1_, products1_.Category_id as Category3_0__, products1_.Id as Id0__ from Categories category0_ inner join Products products1_ on category0_.Id=products1_.Category_id where category0_.Id in (1 , 2);
То же самое (используя будущее) применимо для queryover
или criteria
Этот подход, решение изначально встроено в NHiberante. Это называется:
19.1.5. Использование пакетной выборки
NHibernate может эффективно использовать пакетную выборку, то есть NHibernate может загружать несколько неинициализированных прокси-серверов при доступе к одному прокси-серверу (или коллекциям). класс и уровень коллекции.
Пакетную выборку для классов/сущностей легче понять. Представьте, что у вас есть следующая ситуация во время выполнения: у вас есть 25 экземпляров Cat, загруженных в ISession, каждый Cat имеет ссылку на своего владельца, человека. Класс Person сопоставляется с прокси, lazy="true". Если теперь вы пройдете по всем котам и вызовете cat.Owner для каждого, NHibernate по умолчанию выполнит 25 операторов SELECT, чтобы получить прокси-владельцев. Вы можете настроить это поведение, указав размер партии в отображении Person:
<class name="Person" batch-size="10">...</class>
NHibernate теперь будет выполнять только три запроса, шаблон 10, 10, 5.
Вы также можете включить пакетную выборку коллекций. Например, если у каждого Person есть ленивая коллекция Cats, а 10 человек в настоящее время загружены в ISesssion, итерация по всем людям создаст 10 SELECT, по одному на каждый вызов person.Cats. Если вы включите пакетную выборку для коллекции Cats в отображении Person, NHibernate может выполнять предварительную выборку коллекций:
<class name="Person">
<set name="Cats" batch-size="3">
...
</set>
</class>
ОБЗОР. Существует параметр сопоставления оптимизации: batch-size="25"
.
Мы можем использовать его на уровне класса (используется позже для many-to-one
отношений) или на коллекциях (непосредственно на one-to-many
реальности)
Это приведет к очень небольшому количеству операторов SELECT для загрузки сложного графа объектов. И самое важное преимущество заключается в том, что мы можем использовать разбиение по страницам (Take()
, Skip()
) при запросе корневого объекта (не несколько строк)
Проверьте также это и еще несколько ссылок...