我有一个3级实体层次结构: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),但是当我运行测试以确认Orders和Lines的实体数量(来自NH)时,我得到了错误的结果
我应获取(来自我的测试数据),1xOrder和4xLine,但是,我得到4xOrder和4xLine。 NH似乎没有认识到结果集中的“重复”订单信息组,也没有正确地“重复使用”订单实体。
我正在使用所有整数ID(PK),并且我尝试使用此ID实现T的IComparable和T的IEquatable,希望NH能够看到这些实体的相等性。我也尝试重写Equals和GetHashCode来使用ID。这些“尝试”都没有成功。
NH是否支持“多级提取”操作,如果是,是否需要XML设置(或其他一些机制)来支持它?
注意:我使用了sirocco的解决方案,对我自己的代码进行了一些更改,最终解决了这个问题。对于所有集合,需要将xml从bag更改为set,并且更改了权限本身以实现IComparable&lt;&gt;,这是要设置唯一性的集合的要求。
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字段。这对于新的(瞬态)实体是必需的,否则他们最初将没有ID(我的模型在保存时提供它们)。
答案 0 :(得分:21)
你得到了4XOrder和4XLines,因为与行的连接使结果加倍。您可以在ICriteria上设置Transformer,如:
.SetResultTransformer(new DistinctRootEntityResultTransformer())
答案 1 :(得分:5)
我刚刚阅读了Ayende's Blogpost,他使用了以下示例:
session.CreateCriteria(typeof(Post))
.SetFetchMode("Comments", FetchMode.Eager)
.List();
在条件查询中,以避免在一个特定查询上进行延迟加载
也许这可以帮到你。
答案 2 :(得分:1)
如果您需要保持一对一的行李,那么您可以发出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]时,它们不需要延迟加载。
答案 3 :(得分:0)
@Tigraine:您的查询仅返回带评论的帖子。这使所有帖子都包含所有评论(2个级别)。 Ben要求客户订购LineItem(3级)。 @Ben:据我所知,nHibernate还不支持高达3级的加载。 Hibernate确实支持你。
答案 4 :(得分:0)
我遇到了同样的问题。见thread。我没有得到解决方案,只是来自法比奥的暗示。使用Set而不是bag。它起作用了。
所以我的建议是尝试使用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;
}