NHibernate - 用期货取货

时间:2011-10-06 08:16:57

标签: hibernate nhibernate fluent-nhibernate hql nhibernate-criteria

我有这个Fluent 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. 付款有一个预约
  3. 我试图通过以下限制来获取损失及其付款和储备(但不是状态):

    1. 仅获取至少具有一个状态的损失 “status.Status不在(1,2,7)”。
    2. 仅获取Loss.Payments,其中“loss.Payment.Type = 2 and loss.Payment.Reserve.Status!= 4)”
    3. 仅提取Loss.Reserves,其中Reserve.Status!= 3
    4. 当我试图获取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查询] ---&gt; 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 个答案:

答案 0 :(得分:1)

你有一个连接,但是没有指定你想要的对象,因此你实际上已经取回了一个元组。

您应该在select中指定所需的实体/属性,例如:

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)

答案 1 :(得分:0)

我不倾向于使用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)));

Reserves关联的条件将被添加到左外连接子句中,这意味着只有该关系应用了过滤器。

对于第二个查询,您需要类似的东西,但我不确定您是否可以从左外连接中的另一个表中放置约束(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));

我实际上没有运行这些,但我认为应该大致给你你想要的东西。