Skip to content

级联操作

IBest ORM 支持级联操作,可以在操作主实体时自动处理关联的子实体。

级联类型

typescript
import { CascadeType } from '@ibestservices/ibest-orm';

// 可用的级联类型
CascadeType.Create  // 级联创建
CascadeType.Update  // 级联更新
CascadeType.Delete  // 级联删除
CascadeType.All     // 所有级联操作

配置级联操作

在关联装饰器中配置 cascade 选项:

typescript
import { Table, Column, PrimaryKey, HasMany, CascadeType, ColumnType } from '@ibestservices/ibest-orm';

@Table()
class Author {
  @PrimaryKey()
  id?: number;

  @Column()
  name!: string;

  // 配置级联创建、更新、删除
  @HasMany(() => Book, {
    foreignKey: 'author_id',
    cascade: [CascadeType.Create, CascadeType.Update, CascadeType.Delete]
  })
  books?: Book[];
}

@Table()
class Book {
  @PrimaryKey()
  id?: number;

  @Column()
  title!: string;

  @Column({ name: 'author_id', type: ColumnType.INTEGER })
  authorId?: number;
}

级联创建

使用 insertWithRelations() 方法创建主实体及其关联数据:

typescript
const orm = getORM();

// 创建作者及其书籍
const author = new Author();
author.name = '鲁迅';
author.books = [
  Object.assign(new Book(), { title: '呐喊' }),
  Object.assign(new Book(), { title: '彷徨' })
];

// 级联插入:自动插入作者和关联的书籍
orm.insertWithRelations(author);
// author.id 会被自动赋值
// 书籍的 authorId 会自动设置为 author.id

级联保存

使用 saveWithRelations() 方法智能保存主实体及其关联数据:

typescript
// 新建时级联创建
const author = new Author();
author.name = '老舍';
author.books = [
  Object.assign(new Book(), { title: '骆驼祥子' })
];
orm.saveWithRelations(author);  // 插入作者和书籍

// 更新时级联更新
author.name = '老舍先生';
author.books![0].title = '骆驼祥子(修订版)';
orm.saveWithRelations(author);  // 更新作者和书籍

级联删除

使用 deleteWithRelations() 方法删除主实体及其关联数据:

typescript
// 删除作者时自动删除其所有书籍
orm.deleteWithRelations(author);

注意

orm.query(Author).where({ id: 1 }).delete() 只会删除主表记录,不会级联删除关联数据。 如需级联删除,请使用 deleteWithRelations() 方法。

完整示例

typescript
import {
  Table, Column, PrimaryKey, HasMany, BelongsTo,
  CascadeType, ColumnType, getORM
} from '@ibestservices/ibest-orm';

@Table()
class Department {
  @PrimaryKey()
  id?: number;

  @Column()
  name!: string;

  @HasMany(() => Employee, {
    foreignKey: 'department_id',
    cascade: [CascadeType.Create, CascadeType.Update, CascadeType.Delete]
  })
  employees?: Employee[];
}

@Table()
class Employee {
  @PrimaryKey()
  id?: number;

  @Column()
  name!: string;

  @Column({ name: 'department_id', type: ColumnType.INTEGER })
  departmentId?: number;

  @BelongsTo(() => Department, { foreignKey: 'departmentId' })
  department?: Department;
}

const orm = getORM();
orm.migrate(Department, Employee);

// 1. 级联创建部门和员工
const dept = new Department();
dept.name = '研发部';
dept.employees = [
  Object.assign(new Employee(), { name: '张三' }),
  Object.assign(new Employee(), { name: '李四' })
];
orm.insertWithRelations(dept);

// 2. 级联更新
dept.name = '技术研发部';
dept.employees![0].name = '张三(组长)';
orm.saveWithRelations(dept);

// 3. 查询关联数据
const departments = orm.query(Department).with('employees').find();
console.log(departments[0].employees);  // [{ name: '张三(组长)' }, { name: '李四' }]

// 4. 级联删除
orm.deleteWithRelations(dept);  // 删除部门及其所有员工

注意事项

谨慎使用级联操作

  1. 级联删除会永久删除关联数据,请确保这是预期行为
  2. 级联创建/更新需要在装饰器中配置对应的 CascadeType
  3. 建议在删除前先查询确认要删除的数据
  4. 对于重要数据,考虑使用软删除代替级联删除

软删除替代方案

typescript
import { Table, Column, PrimaryKey, SoftDelete } from '@ibestservices/ibest-orm';

@Table()
class User {
  @PrimaryKey()
  id?: number;

  @Column()
  name!: string;

  @SoftDelete()
  deletedAt?: string;
}

// 软删除 - 不会真正删除数据
orm.query(User).where({ id: 1 }).softDelete();

// 查询时自动排除已软删除的数据
const users = orm.query(User).find();

// 包含已软删除的数据
const allUsers = orm.query(User).withTrashed().find();

// 恢复软删除的数据
orm.query(User).withTrashed().where({ id: 1 }).restore();

最佳实践

  1. 明确配置:只在确实需要级联操作的关联上配置 cascade
  2. 避免深层级联:级联层级不宜过深,避免意外操作大量数据
  3. 使用事务:复杂的级联操作建议使用事务包装
typescript
// 使用事务确保数据一致性
orm.beginTransaction();
try {
  orm.insertWithRelations(author);
  orm.commit();
} catch (e) {
  orm.rollback();
  throw e;
}
  1. 日志记录:重要数据操作前建议记录日志
  2. 软删除优先:对于可能需要恢复的数据,优先使用软删除