延迟加载 (Lazy Loading)
延迟加载是一种按需加载关联数据的技术。当访问关联属性时,ORM 会自动执行查询来获取关联数据。这种方式可以减少初始查询的复杂度。
启用延迟加载
为特定查询启用
typescript
// 为单个查询启用延迟加载
const user = new User();
this.orm.Session(User).Where('id', 1).First(user);
const lazyUser = this.orm.EnableLazyLoading(user);
const bio = this.orm.LoadRelation(lazyUser, 'profile.bio');
const orders = this.orm.LoadRelation(lazyUser, 'orders');
console.log(bio);
console.log(orders.length);
延迟加载API
基础API
typescript
// 为实体启用延迟加载
const lazyUser = orm.EnableLazyLoading(user);
// 加载指定关联
const orders = await orm.LoadRelation(lazyUser, 'orders');
// 检查关联是否已加载
const isLoaded = orm.IsRelationLoaded(lazyUser, 'orders');
// 获取已加载的关联数据
const loadedOrders = orm.GetLoadedRelation(lazyUser, 'orders');
高级功能
预加载关联数据
预加载允许提前加载关联数据,提高后续访问性能。
typescript
// 预加载单个关联
await orm.PreloadRelation(lazyUser, 'profile');
// 批量预加载多个关联
await orm.PreloadRelation(lazyUser, ['orders', 'profile']);
// 现在获取数据会很快,因为已经预加载了
const profile = await orm.GetLoadedRelation(lazyUser, 'profile');
const orders = await orm.GetLoadedRelation(lazyUser, 'orders');
重新加载数据
强制重新从数据库加载数据。
typescript
// 重新加载单个关联
const freshOrders = await orm.ReloadRelation(lazyUser, 'orders');
// 批量重新加载多个关联
const reloadedData = await orm.ReloadRelation(lazyUser, ['orders', 'profile']);
// reloadedData是一个数组,包含所有重新加载的关联数据
嵌套关联加载
支持多层嵌套关联的延迟加载。
typescript
// 加载用户的订单
const orders = await orm.LoadRelation(lazyUser, 'orders');
// 为每个订单加载其订单项
for (const order of orders) {
const lazyOrder = orm.EnableLazyLoading(order, Order);
const items = await orm.LoadRelation(lazyOrder, 'items');
console.log(`订单 ${order.order_no} 的商品数量:`, items.length);
}
支持的关联类型
HasOne 关联
一对一关联,返回单个对象或null。
typescript
@HasOne({
target: () => UserProfile,
foreignKey: 'user_id'
})
profile?: UserProfile;
// 使用
const profile = await orm.LoadRelation(lazyUser, 'profile');
HasMany 关联
一对多关联,返回数组。
typescript
@HasMany({
target: () => Order,
foreignKey: 'user_id'
})
orders?: Order[];
// 使用
const orders = await orm.LoadRelation(lazyUser, 'orders');
BelongsTo 关联
多对一关联,返回单个对象或null。
typescript
@BelongsTo({
target: () => User,
foreignKey: 'user_id'
})
user?: User;
// 使用
const user = await orm.LoadRelation(lazyOrder, 'user');
ManyToMany 关联
多对多关联,返回数组。
typescript
@ManyToMany({
target: () => Role,
through: 'user_roles',
throughForeignKey: 'user_id',
throughOtherKey: 'role_id'
})
roles?: Role[];
// 使用
const roles = await orm.LoadRelation(lazyUser, 'roles');
完整示例
typescript
export class LazyLoadExample {
private orm: IBestORM = GetIBestORM();
async example(): Promise<void> {
// 1. 查询用户
const user = new User('', '');
orm.Session(User).Where('id', 1).First(user);
// 2. 启用延迟加载
const lazyUser = orm.EnableLazyLoading(user);
// 3. 检查关联是否已加载
console.log('orders是否已加载:', orm.IsRelationLoaded(lazyUser, 'orders'));
// 4. 预加载关联数据
await orm.PreloadRelation(lazyUser, ['profile', 'orders']);
// 5. 加载关联数据
const profile = await orm.LoadRelation(lazyUser, 'profile');
const orders = await orm.LoadRelation(lazyUser, 'orders');
console.log('用户档案:', profile?.bio);
console.log('订单数量:', orders?.length || 0);
// 6. 嵌套关联加载
if (orders && orders.length > 0) {
const firstOrder = orders[0];
const lazyOrder = orm.EnableLazyLoading(firstOrder, Order);
const items = await orm.LoadRelation(lazyOrder, 'items');
console.log('第一个订单的商品数量:', items?.length || 0);
}
// 7. 重新加载数据
const freshOrders = await orm.ReloadRelation(lazyUser, 'orders');
// 8. 清除缓存
orm.ClearLazyLoadCache();
}
}
延迟加载 vs 预加载
延迟加载的优势
- 减少初始查询复杂度:只查询主实体数据
- 按需加载:只加载实际使用的关联数据
- 内存效率:避免加载不必要的数据
typescript
// 延迟加载:初始查询简单
const user = new User();
this.orm.Session(User).Where('id', 1).First(user);
const lazyUser = this.orm.EnableLazyLoading(user);
// 访问关联数据查询
const orders = this.orm.LoadRelation(lazyUser, 'orders'); // 按需加载
console.log('用户订单数量:', orders?.length || 0);
预加载的优势
- 避免 N+1 查询:一次性加载所有数据
- 性能可预测:查询次数固定
- 适合批量处理:处理大量数据时更高效
typescript
// 预加载:一次性加载所有数据
const users = await orm.Session(User)
.With(['profile'])
.FindWithRelations();
// 访问关联数据不会触发额外查询
users.forEach(user => {
console.log(user.profile.bio); // 无额外查询
});
与级联操作结合
延迟加载可以与级联操作结合,实现灵活的数据管理:
延迟加载后级联更新
typescript
// 启用延迟加载查询用户
const user = await orm.Session(User)
.Whit('orders')
.Where('id', userId)
.FirstWithRelations();
// 修改关联数据
user.orders[0].product_name = "新商品";
user.orders[0].amount = 299;
// 级联更新
await orm.Save(user, { cascade: true });
性能对比:延迟加载 vs 预加载 vs 级联操作
场景 | 延迟加载 | 预加载 | 级联操作 |
---|---|---|---|
数据展示 | 按需加载,节省内存 | 一次性加载,减少查询 | 不适用 |
数据创建 | 不适用 | 可预加载验证数据 | 自动创建关联 |
数据更新 | 按需加载后更新 | 预加载后批量更新 | 自动更新关联 |
数据删除 | 按需检查关联 | 预加载后批量删除 | 自动删除关联 |