Подтвердить что ты не робот

NHibernate нетерпеливо выбирает несколько уровней

У меня есть трехуровневая иерархия объектов: Customer-Order-Line, которую я хотел бы получить полностью для данного клиента, используя ISession.Get(id). У меня есть следующие фрагменты XML:

customer.hbm.xml:

<bag name="Orders" cascade="all-delete-orphan" inverse="false" fetch="join">
  <key column="CustomerID" />
  <one-to-many class="Order" />
</bag>

order.hbm.xml:

<bag name="Lines" cascade="all-delete-orphan" inverse="false" fetch="join">
  <key column="OrderID" />
  <one-to-many class="Line" />
</bag>

Я использовал атрибут fetch = "join", чтобы указать, что я хочу получить дочерние сущности для каждого родителя, и это построило правильный SQL:

SELECT 
    customer0_.ID AS ID8_2_, 
    customer0_.Name AS Name8_2_, 
    orders1_.CustomerID AS CustomerID__4_, 
    orders1_.ID AS ID4_, 
    orders1_.ID AS ID9_0_, 
    orders1_.PostalAddress AS PostalAd2_9_0_, 
    orders1_.OrderDate AS OrderDate9_0_, 
    lines2_.OrderID AS OrderID__5_, 
    lines2_.ID AS ID5_, 
    lines2_.ID AS ID10_1_, 
    lines2_.[LineNo] AS column2_10_1_, 
    lines2_.Quantity AS Quantity10_1_, 
    lines2_.ProductID AS ProductID10_1_ 

FROM Customer customer0_ 

LEFT JOIN [Order] orders1_ 
       ON customer0_.ID=orders1_.CustomerID 

LEFT JOIN Line lines2_ 
       ON orders1_.ID=lines2_.OrderID 

WHERE customer0_.ID=1

До сих пор это выглядело хорошо - SQL возвращает правильный набор записей (только с одним отдельным orderid), но когда я запускаю тест для подтверждения правильного количества сущностей (из NH) для ордеров и строк, я получаю неправильные результаты

Я должен получать (из моих тестовых данных), 1xOrder и 4xLine, однако я получаю 4xOrder и 4xLine. Похоже, что NH не признает "повторяющуюся" группу информации о заказе в результирующем наборе и не правильно "повторно использует" объект Order.

Я использую все целые идентификаторы (PK), и я попытался реализовать IComparable из T и IEquatable из T с использованием этого идентификатора, в надежде, что NH увидит равенство этих объектов. Я также попробовал переопределить Equals и GetHashCode, чтобы использовать идентификатор. Ни одна из этих "попыток" не удалась.

Является ли "множественная выровненная выборка" поддерживаемой операцией для NH, и если да, требуется ли XML-параметр (или какой-либо другой механизм) для его поддержки?


NB: Я использовал решение sirocco с несколькими изменениями в моем собственном коде, чтобы окончательно решить эту проблему. xml необходимо изменить из пакета для установки, для всех коллекций и самими правами были изменены для реализации IComparable < > , что является требованием набора для уникальности, который должен быть установлен.

public class BaseEntity : IComparable<BaseEntity>
{
    ...

    private Guid _internalID { get; set; }
    public virtual Guid ID { get; set; }

    public BaseEntity()
    {
        _internalID = Guid.NewGuid();
    }

    #region IComparable<BaseEntity> Members

    public int CompareTo( BaseEntity other )
    {
        if ( ID == Guid.Empty || other.ID == Guid.Empty )
            return _internalID.CompareTo( other._internalID );

        return ID.CompareTo( other.ID );
    }

    #endregion

    ...

 }

Обратите внимание на использование поля InternalID. Это необходимо для новых (переходных) объектов, иначе они не будут иметь идентификатор изначально (моя модель предоставила их при сохранении).

4b9b3361

Ответ 1

Вы получаете 4XOrder и 4XLines, потому что соединение с линиями удваивает результаты. Вы можете установить трансформатор на ICriteria, например:

.SetResultTransformer(new DistinctRootEntityResultTransformer())

Ответ 2

Я просто прочитал Ayende Blogpost, где использовал следующий пример:

session.CreateCriteria(typeof(Post))
    .SetFetchMode("Comments", FetchMode.Eager)
    .List();

В запросе критерия, чтобы избежать ленивой загрузки на одном конкретном запросе

Может быть, это может вам помочь.

Ответ 3

Если вам нужно сохранить свой маникюр в виде мешков, вы можете выпустить 2 запроса, каждый из которых имеет только 1 уровень иерархии. например, что-то вроде этого:

var temp = session.CreateCriteria( typeof( Order ) )
    .SetFetchMode( "Lines", NHibernate.FetchMode.Eager )
    .Add( Expression.Eq( "Customer.ID", id ) )
    .List();

var customer = session.CreateCriteria( typeof( Customer ) )
    .SetFetchMode( "Orders", NHibernate.FetchMode.Eager )
    .Add( Expression.Eq( "ID", id ) )
    .UniqueResult();

Линии загружаются в кеш-память NH в первом запросе, поэтому им не потребуется ленивая загрузка при последующем доступе, например, customer.Orders [0].Lines [0].

Ответ 4

@Tigraine: ваш запрос возвращает сообщение только с комментариями. Это приносит все сообщения со всеми комментариями (2 уровня). То, что Бен спрашивает, - это Заказчик для заказа на LineItem (3 уровня). @Ben: Насколько я знаю, nHibernate не поддерживает загрузку до 3 уровня. Hibernate действительно поддерживает это.

Ответ 5

У меня была такая же проблема. Смотрите thread. Я не получил решения, но намек от Фабио. Используйте Set вместо мешка. И это сработало.

Итак, мое предложение - попробовать использовать set. Вам не нужно использовать коллекцию Iesi, использующую IDictonary, и NH рада

public override IEnumerable<Baseline> GetAll()
{
     var baselines = Session.CreateQuery(@" from Baseline b
                                            left join fetch b.BaselineMilestones bm
                                            left join fetch bm.BaselineMilestonePrevious ")
                                            .SetResultTransformer(Transformers.DistinctRootEntity)
                                            .List<Baseline>();
     return baselines;
}