Win10环境借助DockerDesktop部署大数据时序数据库Apache Druid的操作做法实用指南
2026-09-15
2026-09-21 0
NHibernate 2.1.2 的延迟加载通过代理对象和持久化集合实现:先在 .hbm.xml 中保持实体/关联的 lazy 设置,再让实体类及可延迟成员能被代理,最后在 ISession 开启时访问这些成员。若应当把数据带出 Session,应在关闭前显式初始化、采用 join fetch 一次取齐,或转成 DTO,不要把已脱离 Session 的代理留给视图层猜。
session.Load(id) 可先返回只含标识的代理,访问普通属性时再查数据库。Order.Customer,通常通过代理延迟取对端实体。Order.Lines,先持有持久化集合包装,枚举、读取数量或执行其他需要内容的操作时再发 SQL。NHibernate 官方 2.1 系列文档指出,默认获取计划会对集合采用 lazy select,对实体采用 lazy proxy。具体项目仍应以自己的映射文件为准,因为顶层 default-lazy 或局部 lazy="false" 会改变行为。
public class Order
{
public virtual int Id { get; protected set; }
public virtual DateTime CreatedAt { get; set; }
public virtual Customer Customer { get; set; }
public virtual ISet Lines { get; protected set; }
}
在常见的子类代理方案中,类不能是 sealed,需延迟拦截的成员应为 virtual。实体的无参构造函数至少应让 NHibernate 能够调用。
lazy="true" 使实体可通过代理延迟初始化;many-to-one lazy="proxy" 让单值关联使用代理;集合上的 lazy="true" 则让集合内容在需要时查询。映射必须在构建 SessionFactory 前加载。
using (ISession session = sessionFactory.OpenSession())
{
Order order = session.Load(42);
// 通常只有标识时不需初始化代理
Console.WriteLine(order.Id);
// 访问普通属性后,代理需要加载 Order
Console.WriteLine(order.CreatedAt);
// 枚举集合时再加载 OrderLines
foreach (OrderLine line in order.Lines)
Console.WriteLine(line.Quantity);
}
验证时开启 NHibernate SQL 日志,关注查询是在哪一行访问之后出现。不要只凭断点中对象已经有值就判断延迟失效,调试器展开属性本身就可能触发加载。
Order order;
using (ISession session = sessionFactory.OpenSession())
{
order = session.Get(42);
NHibernateUtil.Initialize(order.Customer);
NHibernateUtil.Initialize(order.Lines);
}
这适合已知下一层确实应当这些关联的场景。不要对所有关联无条件初始化,否则只是把延迟加载改成了隐蔽的全量加载。
Order order = session.CreateQuery(@"
select distinct o
from Order o
left join fetch o.Lines
where o.Id = :id")
.SetInt32("id", 42)
.UniqueResult();
join fetch 是查询级获取计划,不用为了某个页面就把全局映射改成 lazy="false"。对多个集合同时 join fetch 可能造成结果集笛卡尔放大,应按真实用例分解查询。
列表页只需要订单时间和行数时,直接查询所需列并返回 DTO,比将完整实体图交给已脱离 Session 的界面层更清楚。
ISession 是否已关闭或清理。NHibernate 2.1.2 延迟加载要同时满足映射、可代理类设计和 Session 生命周期三个条件。正确使用时,默认保持 lazy,再由每个业务用例明确决定哪些关联需在 Session 内初始化,并用 SQL 日志确认没有引入 N+1。