繁体   English   中英

NHibernate意外的惰性属性加载

[英]NHibernate unintentional lazy property loading

我介绍了一个业务对象的映射,该对象具有(除其他外)一个名为“名称”的属性:

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

由于某些原因,当我获取“ Foo”对象时, NHibernate似乎会应用惰性属性加载 (对于简单属性,而不是关联):

以下代码段生成n + 1条 SQL语句,其中第一个仅获取ID,其余n条获取每个记录的Name:

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);

这篇文章之后“懒惰属性...很少是启用...(和)在Hibernate 3中的重要功能,默认情况下处于禁用状态。”

那我在做什么错? 我通过执行NHibernateUtil.Initialize(foo)设法解决了LazyLoadingException,但更糟糕的是n + 1条 sql语句使我的应用屈膝。

映射如下所示:

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

顺便说一句:抽象的“ BusinessObjectBase”基类封装了ID属性,该属性用作内部标识符。

我不认为这是由于延迟加载属性造成的。 而是因为使用了EnumerableLoad

查看有关Enumerable参考文档

...迭代器将使用初始SQL查询返回的标识符按需加载对象(n + 1个总数)

使用批处理来减少查询数量(在类的映射中)

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

...或使用List而不是可枚举:

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();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM