最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
NHibernate 2.1.2 延迟加载配置与验证教程
时间:2026-09-21 19:36:01 编辑:袖梨 来源:一聚教程网
NHibernate 2.1.2 的延迟加载通过代理对象和持久化集合实现:先在 .hbm.xml 中保持实体/关联的 lazy 设置,再让实体类及可延迟成员能被代理,最后在 ISession 开启时访问这些成员。若应当把数据带出 Session,应在关闭前显式初始化、采用 join fetch 一次取齐,或转成 DTO,不要把已脱离 Session 的代理留给视图层猜。
延迟加载的三个对象
- 实体代理:
session.Load<Order>(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<OrderLine> Lines { get; protected set; }
}
在常见的子类代理方案中,类不能是 sealed,需延迟拦截的成员应为 virtual。实体的无参构造函数至少应让 NHibernate 能够调用。
在 hbm.xml 中配置延迟关联
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Shop.Domain"
namespace="Shop.Domain">
<class name="Order" table="Orders" lazy="true">
<id name="Id" column="OrderId">
<generator class="native" />
</id>
<property name="CreatedAt" not-null="true" />
<many-to-one name="Customer"
class="Customer"
column="CustomerId"
lazy="proxy"
not-null="true" />
<set name="Lines"
table="OrderLines"
inverse="true"
lazy="true">
<key column="OrderId" />
<one-to-many class="OrderLine" />
</set>
</class>
</hibernate-mapping>
lazy="true" 使实体可通过代理延迟初始化;many-to-one lazy="proxy" 让单值关联使用代理;集合上的 lazy="true" 则让集合内容在需要时查询。映射必须在构建 SessionFactory 前加载。
用 SQL 日志验证载入时机
using (ISession session = sessionFactory.OpenSession())
{
Order order = session.Load<Order>(42);
// 通常只有标识时不需初始化代理
Console.WriteLine(order.Id);
// 访问普通属性后,代理需要加载 Order
Console.WriteLine(order.CreatedAt);
// 枚举集合时再加载 OrderLines
foreach (OrderLine line in order.Lines)
Console.WriteLine(line.Quantity);
}
验证时开启 NHibernate SQL 日志,关注查询是在哪一行访问之后出现。不要只凭断点中对象已经有值就判断延迟失效,调试器展开属性本身就可能触发加载。
数据必须离开 Session 时
方法一:在关闭前显式初始化
Order order;
using (ISession session = sessionFactory.OpenSession())
{
order = session.Get<Order>(42);
NHibernateUtil.Initialize(order.Customer);
NHibernateUtil.Initialize(order.Lines);
}
这适合已知下一层确实应当这些关联的场景。不要对所有关联无条件初始化,否则只是把延迟加载改成了隐蔽的全量加载。
方法二:为本次用例使用 join fetch
Order order = session.CreateQuery(@"
select distinct o
from Order o
left join fetch o.Lines
where o.Id = :id")
.SetInt32("id", 42)
.UniqueResult<Order>();
join fetch 是查询级获取计划,不用为了某个页面就把全局映射改成 lazy="false"。对多个集合同时 join fetch 可能造成结果集笛卡尔放大,应按真实用例分解查询。
方法三:在 Session 内投影为 DTO
列表页只需要订单时间和行数时,直接查询所需列并返回 DTO,比将完整实体图交给已脱离 Session 的界面层更清楚。
LazyInitializationException 怎么查
- 确认异常中是哪个代理或集合未初始化。
- 确认访问发生时
ISession是否已关闭或清理。 - 如果该数据是用例必需,在业务边界内用显式初始化、join fetch 或 DTO 取得。
- 如果不是必需,删除对该导航属性的隐式访问,特别是序列化器、日志和视图模板的自动属性遍历。
- 修改后重新检查 SQL 数量,避免从一个异常变成 N+1 查询。
总结
NHibernate 2.1.2 延迟加载要同时满足映射、可代理类设计和 Session 生命周期三个条件。正确使用时,默认保持 lazy,再由每个业务用例明确决定哪些关联需在 Session 内初始化,并用 SQL 日志确认没有引入 N+1。
相关文章
- AI Coding 接入企业内网:从浏览器 Token 过渡到官方 MCP 的权限治理 09-21
- 把MCP看作AI时代的USB-C:开发者为何都在讨论它 09-21
- 如何打造对 Agent 友好的网站 09-21
- Thinkphp5分页后携带参数跳转传递功能实现 09-21
- 用 RAG 扩展答疑机器人的知识边界 09-21
- RAG 重排序优化:如何把检索准确率从 70% 提升至 90% 09-21