NHibernate无意的懒惰属性加载

时间:2010-06-02 09:33:26

标签: c# performance nhibernate lazy-loading

我为业务对象引入了一个映射,其中包含一个名为“Name”的属性:

public class Foo : BusinessObjectBase
{
    ...
    public virtual string Name { get; set; }
}

出于某种原因,当我获取“Foo”对象时, NHibernate似乎应用了延迟属性加载(对于简单属性,而不是关联):

以下代码片段生成 n + 1 SQL语句,其中第一个只获取id,剩余的 n 获取每个记录的名称:

ISession session = ...IQuery query = session.CreateQuery(queryString);
ITransaction tx = session.BeginTransaction();

List<Foo> result = new List<Foo>();
foreach (Foo foo in query.Enumerable())
{
    result.Add(foo);
}

tx.Commit();
session.Close();

产生

select foo0_.FOO_ID as col_0_0_ from V1_FOO foo0_
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_ 
    WHERE foo0_.FOO_ID=:p0;:p0 = 81
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_ 
    WHERE foo0_.FOO_ID=:p0;:p0 = 36470
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_ 
    WHERE foo0_.FOO_ID=:p0;:p0 = 36473

类似地,以下代码在会话关闭后导致LazyLoadingException:

ISession session = ...
ITransaction tx = session.BeginTransaction();
Foo result = session.Load<Foo>(id);
tx.Commit();
session.Close();

Console.WriteLine(result.Name);

关注this post“延迟属性...很少是启用...(和)在Hibernate 3中的重要功能,默认情况下处于禁用状态。”

那么我做错了什么?我设法通过执行NHibernateUtil.Initialize(foo)来解决LazyLoadingException,但更糟糕的是 n + 1 sql使我的申请陷入困境的陈述。

这是映射的样子:

<class name="Foo" table="V1_FOO">
    ...
    <property name="Name" column="NAME"/>
</class>

BTW:抽象的“BusinessObjectBase”基类封装了作为内部标识符的ID属性。

1 个答案:

答案 0 :(得分:6)

我认为这不是由于懒惰的属性加载。这是因为使用了EnumerableLoad

查看关于Enumerable的{​​{3}}:

  

...迭代器将加载对象   需求,使用返回的标识符   通过初始SQL查询(n + 1选择   总)

使用批量提取来减少查询次数(在类的映射中)

<class name="Foo" table="V1_FOO" batch-size="20">

...或使用List代替Enumerable:

IQuery query = session.CreateQuery(queryString);
List<Foo> result query.List<Foo>();

注意:Enumerable只有在您不希望需要整个结果时才有意义,或者在您不希望同时将它们全部存储在内存中的特殊情况下(那么您需要) Evict删除它们)。在大多数情况下,List就是您所需要的。


Load的情况下,仅创建代理(不执行查询)。在第一次访问它时,它被加载。 (这非常强大,例如在查询中将此代理用作过滤器参数或将其链接到另一个实体而无需加载其内容。)如果您需要其内容,请使用Get代替

using (ISession session = ...)
using (ITransaction tx = session.BeginTransaction())
{
    Foo result = session.Get<Foo>(id);

    tx.Commit();
}
// could still fail in case of lazy loaded references
Console.WriteLine(result.Name);

......甚至更好,只在会话开放时使用实体。

using (ISession session = ...)
using (ITransaction tx = session.BeginTransaction())
{
    Foo result = session.Load<Foo>(id);
    // should always work fine
    Console.WriteLine(result.Name);

    tx.Commit();
}