NHibernate Multiquery для быстрой загрузки без объединений - PullRequest
2 голосов
/ 12 апреля 2011

Можно ли использовать мультизапрос и иметь два hql-запроса, возвращающие два разных набора сущностей, где один из наборов используется в другом, и что сеанс "исправляет" это через кэш первого уровня?

например. сценарий (тупой, и его можно решить с помощью объединений)

public class Room
{
  ...
  public virtual ISet<Bookings> Bookings {get;set;}
  public virtual bool IsAvailible {get;set;}
  ...
}

public class Booking
{
  ...
}

После выполнения многокритериального с двумя hql:

  1. возвращая все комнаты, где IsAvailible = true
  2. возврат всех заказов, в которых есть комната, в которой есть IsAvailible

при доступе к комнате из результата и его бронирований я хочу, чтобы они были разрешены из второго набора результатов через кэш первого уровня сеанса и там, избегая n + 1.

Ответы [ 2 ]

7 голосов
/ 12 апреля 2011

Вообще говоря, NHibernate может использовать кеш для «объединения» результатов запросов, выполненных через Multiquery.Однако следует отметить, что это обычно относится только к случаям, когда ленивые коллекции загружаются без каких-либо ограничений.

Примеры:

Invoice iAlias = null;
InvoiceDetails idAlias = null;

// Base-Query: get Invoices with certain condition
var invoices = session.QueryOver<Invoice>()
    .Where(i => i.Number == "001")
    .Future<Invoice>();

// Option 1: this will still cause N+1 if we iterate through invoices,
// because it doesn't know better
var invoicedetails = session.QueryOver<InvoiceDetails>()
    .JoinAlias(a => a.Invoice, () => iAlias)
    .Where(() => iAlias.Number == "001")
    .Future<InvoiceDetails>();

// Option 2: this will still cause N+1 if we iterate through invoices,
// because we limited the possible results using a where-condition
var invoices2 = session.QueryOver<Invoice>()
    .Left.JoinAlias(i => i.Details, () => idAlias)
    .Where(i => i.Number == "001")
    .And(() => idAlias.Quantity > 5)
    .Future<Invoice>();

// Option 3: this will work without N+1, because we don't use a filter
// -> NHibernate will use the collection in cache
var invoices3 = session.QueryOver<Invoice>()
    .Left.JoinAlias(i => i.Details, () => idAlias)
    .Where(i => i.Number == "001")
    .Future<Invoice>();

foreach (Invoice i in invoices)
{
    int count = i.Details.Count;
}

Если мы закомментируем два из трех вариантов ивыполнив код, мы увидим, что только вариант 3 будет препятствовать N + 1, другие два все равно будут загружать InvoiceDetails для каждого Invoice в цикле.

Конечно, это очень простопример и очевидно, что вариант 3 также может быть выполнен без Base-запроса и по-прежнему возвращать тот же результат, но я надеюсь, что вы поняли.

В случае, когда мы загружаем два разных набора сущностей,т.е. корневой класс отличается от варианта 1, это «объединение», скорее всего, не будет работать.

Извините, если я использовал QueryOver вместо HQL, но применяются те же правила.

0 голосов
/ 18 марта 2015

Гюс, имейте в виду, что иногда у вас могут быть похожие проблемы, потому что LeftOuterJoin не установлен.

.JoinAlias ​​(x => x.Prop, () => propAlias, JoinType.LeftOuterJoin)

...