NHibernate - получение с фьючерсами - PullRequest
2 голосов
/ 06 октября 2011

У меня есть это свободное отображение NHibernate:

public LossMap()
{
    Table("losses");
    Id(x => x.Id).Column("id");
    References(x => x.Policy).Column("pol_id");
    HasMany(x => x.Statuses).KeyColumn("loss_id").Cascade.All().Inverse();
    HasMany(x => x.Reserves).KeyColumn("loss_id").Cascade.All().Inverse();
    HasMany(x => x.Payments).KeyColumn("loss_id").Cascade.All().Inverse();
}

public LossPaymentMap()
{
    Table("losspayments");
    Id(x => x.Id).Column("id");
    Map(x => x.Type).Column("type_id");
    References(x => x.Reserve).Column("reserve_id");
}

public LossReserveMap()
{
    Table("lossreserves");
    Id(x => x.Id).Column("id");
    Map(x => x.Type).Column("type_id");
    Map(x => x.Status).Column("status_id");
    References(x => x.ParentReserve).Column("parent_reserve_id");
}

public LossStatusMap()
{
    Table("lossstatuses");
    Id(x => x.Id).Column("id");
    Map(x => x.Status).Column("status_id");
    Map(x => x.ExpirationDate).Column("expirationdate");
    References(x => x.Loss).Column("loss_id");
}

Подведем итог:

  1. У потери много платежей, резервов и статусов
  2. Оплата имеет один резерв

Я пытаюсь получить убытки и их выплаты и резервы (но не статусы) со следующими ограничениями:

  1. Выбирать только те потери, которые имеют хотя бы один статус с msgstr "status.Status not in (1,2,7)".
  2. Только получать Loss.Payments, где "loss.Payment.Type = 2 и loss.Payment.Reserve.Status! = 4)"
  3. Только получать потери. Запасы, где Резерв.Статус! = 3

Поскольку я пытаюсь получить 2 параллельных отношения, я должен использовать множественные запросы или фьючерсы, чтобы избежать декартового произведения (верно?), Как объяснено здесь: http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate

Я пришел с этим запросом (на HQL):

int[] statuslist = new int[3] {1, 2, 7};

var losses =
session.CreateQuery(
    "from Loss l left join fetch l.Payments as payment join l.Statuses as status where l.Policy.Product.Id = :tid1 " + 
    "and status.Status not in ( :statuslist1) " +
    "and payment.Type = 2 and payment.Reserve.Status != 4")
    .SetParameter("tid1", productid)
    .SetParameterList("statuslist1", statuslist)
    .Future<Loss>();

session.CreateQuery(
    "from Loss l left join fetch l.Reserves as reserve join l.Statuses as status where l.Policy.Product.Id = :tid2 " +
    "and status.Status not in ( :statuslist2) " +
    "and reserve.Status != 3 ")
    .SetParameter("tid2", productid)
    .SetParameterList("statuslist2", statuslist)
    .Future<Loss>();

var list = losses.ToList();

Однако при выполнении этого запроса я получаю сообщение об ошибке: NHibernate.HibernateException: не удалось выполнить мультизапрос [.. SQL запрос] ---> System.ArgumentException: значение «System.Object []» не имеет введите "Entities.Loss" и не может использоваться в этой универсальной коллекции.

Есть какие-нибудь подсказки, что я здесь не так делаю?

Когда я снимаю ограничение статуса, запрос работает:

var losses =
session.CreateQuery(
    "from Loss l left join fetch l.Payments as payment where l.Policy.Product.Id = :tid1 " + 
    "and payment.Type = 2 and payment.Reserve.Status != 4")
    .SetParameter("tid1", productid)
    .Future<Loss>();

session.CreateQuery(
    "from Loss l left join fetch l.Reserves as reserve where l.Policy.Product.Id = :tid2 " +
    "and reserve.Status != 3 ")
    .SetParameter("tid2", productid)
    .Future<Loss>();

Однако результаты не те, которые я хочу (мне нужно это ограничение).

Любой совет?

О, и использование HQL не является обязательным, если это возможно с помощью Linq или QueryOver, у меня нет проблем с этим.

Спасибо!

Ответы [ 2 ]

1 голос
/ 11 октября 2011

У вас есть соединение, но вы не указали, какой объект вам нужен, поэтому вы на самом деле возвращаете кортеж.

Вы должны указать, какие объекты / свойства вы хотите выбрать, например:

select l
from Loss l left join fetch l.Payments as payment
where l.Policy.Product.Id = :tid1
and payment.Type = 2 and payment.Reserve.Status != 4

Следует также отметить, что при использовании объединения вы можете получить несколько результатов для одной и той же сущности, если вам нужны только уникальные сущности, вы должны использовать Transformers.DistinctRootEntity

Для IQuery / ICriteria: .SetResultTransformer(Transformers.DistinctRootEntity)
Для QueryOver: .TransformUsing(Transformers.DistinctRootEntity)

0 голосов
/ 06 октября 2011

Я не склонен использовать HQL, поэтому не могу дать вам совет, но я не уверен, что приведенный выше запрос - именно то, что вам нужно.Вы хотите получить все потери независимо от критериев дочерних объектов?Приведенный выше запрос вернет только те потери, которые соответствуют критериям, установленным для дочерних объектов.Похоже, вы хотите вернуть все потери, которые соответствуют основным критериям, но отфильтровать дочерние коллекции по ним?Под этим я подразумеваю, что с учетом текущего запроса, если у вас есть убыток, который имеет статус 2, но не имеет платежей с типом платежа 2, сущность убытков не будет возвращена из запроса.Вместо этого я думаю, что вам нужно применить фильтр к объединению, чтобы объект потери возвращался из запроса, но сбор платежей пуст.Например, что-то вроде этого для первого запроса:

int[] values = new int[] { 1,2,3};
var query1 = session.CreateCriteria<Trade>()
                    .CreateAlias("Status", "s").Add(Expression.Not(Expression.In("s.Status", values)))
                    .CreateAlias("Reserves", "r", JoinType.LeftOuterJoin, Expression.Not(Expression.Eq("r.Status", 3)));

Критерии ассоциации резервов будут добавлены в предложение левого внешнего соединения, означающее, что фильтр применяется только к этому отношению.Во втором запросе вам нужно что-то похожее, но я не уверен, что вы можете поместить ограничения из другой таблицы в левое внешнее соединение (Payment.Reserve.Status! = 4).Для этого вы можете использовать подзапрос.Что-то вроде:

DetachedCriteria paymentSubQuery = null; //make a query for getting all payments with type 2 and reserve.Status != 4

var query2 = session.CreateCriteria<Trade>()
                    .CreateAlias("Status", "s").Add(Expression.Not(Expression.In("s.Status", values)))
                    .CreateAlias("Payments", "p", JoinType.LeftOuterJoin).Add(Subqueries.PropertyIn("p.Id", paymentSubQuery));

Я на самом деле не запускал их, но думаю, что это должно примерно дать вам то, что вы хотите.

...