3

私はこの流暢な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つの予約があります

次の制約を使用して、損失とその支払いおよび準備金(ステータスではない)を取得しようとしています。

  1. 「status.Statusnotin(1,2,7)」のステータスが少なくとも1つある損失のみをフェッチします。
  2. Loss.Paymentsをフェッチするのは、「loss.Payment.Type = 2およびloss.Payment.Reserve.Status!= 4)」の場合のみです。
  3. Reserve.Status!=3の場合にのみLoss.Reservesをフェッチします

2つの並列関係を取得しようとしているので、ここで説明されているように、デカルト積を回避するためにマルチクエリまたは先物を使用する必要があります(右?):http://ayende.com/blog/4367/eagerly-loading-entity-associations- 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[]」はタイプ「エンティティ」ではありません.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を使用して可能であれば、問題はありません。

ありがとう!

4

2 に答える 2

1

結合はありますが、必要なオブジェクトを指定していないため、実際にはタプルが返されます。

選択で必要なエンティティ/プロパティを指定する必要があります。例:

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)

于 2011-10-11T11:35:08.100 に答える
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アソシエーションの基準は、左側の外部結合句に追加されます。これは、その関係にのみフィルターが適用されることを意味します。

2番目のクエリには同様のものが必要ですが、左側の外部結合(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));

私は実際にこれらを実行していませんが、それはあなたが望むものを大まかに与えるはずだと思います。

于 2011-10-06T11:53:26.700 に答える