我试图找出为什么在没有过滤的情况下返回子集合,即使在急切加载集合并且生成的SQL是正确的时候也是如此。
课程的流利映射是:
public class OptionIdentifierMap : ClassMap<OptionIdentifier>
{
public OptionIdentifierMap()
: base("OptionIdentifier")
{
//Id Mapping Removed
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None();
}
}
public class OptionPriceMap : ClassMap<OptionPrice>
{
public OptionPriceMap()
: base("OptionPrice")
{
//Id Mapping removed
References(x => x.Option)
.Column("OptionIdentifier_id")
.Cascade.None()
.ForeignKey("FK_OptionPrice_OptionIdentifier_id_OptionIdentifier_Id")
.Not.Nullable();
References(x => x.Increment)
.Column("PricingIncrement_id")
.Cascade.None()
.ForeignKey("FK_OptionPrice_PricingIncrement_id_PricingIncrement_Id")
.Not.Nullable();
Map(x => x.Price).Not.Nullable();
}
}
和PricingIncrement mapping
public class PricingIncrementMap : ClassMap<PricingIncrement>
{
public PricingIncrementMap()
: base("PricingIncrement")
{
Map(x => x.IncrementYear);
HasMany<OptionPrice>(x => x.Options)
.KeyColumn("PricingIncrement_id")
.Cascade.None().Inverse();
}
}
实体是:
public class PricingIncrement : Entity
{
public PricingIncrement()
{
Options = new List<OptionPrice>();
}
public virtual int IncrementYear { get; set; }
public virtual IList<OptionPrice> Options { get; set; }
}
public class OptionPrice : Entity
{
public OptionPrice()
{
}
public virtual OptionIdentifier Option { get; set; }
public virtual PricingIncrement Increment { get; set; }
public virtual float Price { get; set; }
}
public class OptionIdentifier : Entity
{
public OptionIdentifier()
{
OptionPrices = new List<OptionPrice>();
}
public virtual IList<OptionPrice> OptionPrices { get; set; }
}
我试图查询具有特定PricingIncrement的optionprice值的所有OptionIdentifier。 nhibernate根据我的标准生成的SQL查询是:
SELECT this_.Id as Id37_4_,
.......
FROM OptionIdentifier this_ inner join OptionPrice op2_ on this_.Id = op2_.OptionIdentifier_id
inner join PricingIncrement i3_ on op2_.PricingIncrement_id = i3_.Id
WHERE (this_.IsDeleted = 0)
and this_.Id in (7)
and i3_.IncrementYear = 2015
我用来构建此查询的标准是:
ICriteria pagedCriteria = this.Session.CreateCriteria<OptionIdentifier>()
.CreateAlias("OptionPrices", "op", JoinType.InnerJoin)
.CreateAlias("op.Increment", "i", JoinType.InnerJoin)
.SetFetchMode("op", FetchMode.Eager)
.SetFetchMode("i", FetchMode.Eager)
.Add(Restrictions.Eq("i.IncrementYear", 2015))
.Add(Expression.In("Id", idList.ToList<int>()))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity);
当查看SQL事件探查器时,执行查询并且结果是正确的,我在OptionPrice表中为每个子项获取一行符合条件(在我的情况下为1),来自与OptionIdentifier匹配的可用4行(那里)在PricingIncrement中有4行,在OptionPrice中有4行,对于OptionIdentifier_id的每个PricingIncrement都有一行7)
但是当我尝试迭代集合以获取某些值时,由于某种原因,nhibernate正在加载子集合,就像指定了延迟加载一样,并加载了完整的4个子行。阅读文档FetchMode应该修复这个,防止nhibernate延迟加载子集合。类似于N + 1常见问题。
我检查了SQL事件探查器以查看发生了什么,并且当我尝试访问时,nhibernate正在生成没有原始过滤器的查询来填充子集合。如果我不访问该集合,则不会生成任何查询。
做一些测试我尝试了不同的连接类型和获取模式,到目前为止,迭代集合而没有hibernate加载所有元素的唯一方法是在连接类型中指定LeftOuterJoin,但这意味着不同的东西。
我试图搜索类似的问题,但所有人都说急切加载应该有效,或者提到我应该使用过滤器。到目前为止我还没有找到任何答案。
非常感谢任何帮助。
答案 0 :(得分:1)
我想分享我的方法,也许不是答案......
one-to-many
(收藏) 在创建任何类型的复杂查询(ICriteria,QueryOver)时,我们应该仅在 start 架构上使用(LEFT) JOIN。即在 many-to-one
(流利的References()
)。这导致从分页的角度来看预期的行数(每个根实体总是只有一行)
为了避免集合的1 + N问题(但实际上甚至是多对一的)我们有NHiberante强大的功能:
<强> 19.1.5. Using batch fetching 强>
NHibernate可以有效地使用批量提取,也就是说,如果访问一个代理(或集合,则可以加载几个未初始化的代理。批量提取是惰性选择提取策略的优化)。 ..
在这里阅读更多内容:
所以,在我们的例子中,我们会像这样调整映射:
public PricingIncrementMap()
: base("PricingIncrement")
{
Map(x => x.IncrementYear);
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse() // I would use .Inverse() as well
// batch fetching
.BatchSize(100);
}
因此,我们设法避免1 + N问题,我们也只查询星型模式。现在,我们如何才能加载我们收藏的过滤物品集?好吧,我们有本机和非常强大的NHibernate功能:
<强> 18.1. NHibernate filters. 强>
NHibernate增加了预定义过滤条件的能力,并在类和集合级别附加这些过滤器。过滤条件是能够定义一个与类和现有的“where”属性非常类似的限制条款以及各种集合元素...
在这里阅读更多相关信息:
因此,在我们的例子中,我们将定义过滤器
public class CollFilter : FilterDefinition
{
public CollFilter()
{
WithName("CollFilter")
.WithCondition("PricingIncrement_id = :pricingIncrementId")
.AddParameter("pricingIncrementId",NHibernate.Int32);
}
}
我们需要再次扩展我们的映射:
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse()
// batch fetching
.BatchSize(100)
// this filter could be turned on later
.ApplyFilter<CollFilter>();
现在,在执行我们的查询之前,我们只需要启用该过滤器并提供2015年的正确ID:
// the ID of the PricingIncrement with year 2015
var pricingIncrementId thes.Session
.QueryOver<PricingIncrement>()
.Where(x => x.IncrementYear == 2015)
.Take(1)
.Select(x => x.ID)
.SingleOrDefault<int?>();
this.Session
.EnableFilter("CollFilter")
.SetParameter("pricingIncrementId", pricingIncrementId);
// ... the star schema query could be executed here
最后,我们可以使用子查询来限制我们的查询返回的根实体的数量。
15.8. Detached queries and subqueries
在这里阅读更多相关信息:
所以,我们的子查询可能是
// Subquery
var subquery = DetachedCriteria.For<OptionPrice >()
.CreateAlias("Increment", "i", JoinType.InnerJoin)
.Add(Restrictions.Eq("i.IncrementYear", 2015))
.SetProjection(Projections.Property("Option.ID"));
// root query, ready for paging, and still filtered as wanted
ICriteria pagedCriteria = this.Session.CreateCriteria<OptionIdentifier>()
.Add(Subqueries.PropertyIn("ID", subquery))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity);
总结:我们可以使用NHibernate附带的许多功能。他们是有原因的。通过它们,我们可以实现稳定可靠的代码,可以进一步扩展(首先是分页)
注意:也许我做了一些拼写错误......但总体思路应该是明确的