Skip to content

延迟加载 (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 预加载

延迟加载的优势

  1. 减少初始查询复杂度:只查询主实体数据
  2. 按需加载:只加载实际使用的关联数据
  3. 内存效率:避免加载不必要的数据
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);

预加载的优势

  1. 避免 N+1 查询:一次性加载所有数据
  2. 性能可预测:查询次数固定
  3. 适合批量处理:处理大量数据时更高效
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 级联操作

场景延迟加载预加载级联操作
数据展示按需加载,节省内存一次性加载,减少查询不适用
数据创建不适用可预加载验证数据自动创建关联
数据更新按需加载后更新预加载后批量更新自动更新关联
数据删除按需检查关联预加载后批量删除自动删除关联