级联操作
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); // 删除部门及其所有员工注意事项
谨慎使用级联操作
- 级联删除会永久删除关联数据,请确保这是预期行为
- 级联创建/更新需要在装饰器中配置对应的
CascadeType - 建议在删除前先查询确认要删除的数据
- 对于重要数据,考虑使用软删除代替级联删除
软删除替代方案
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();最佳实践
- 明确配置:只在确实需要级联操作的关联上配置
cascade - 避免深层级联:级联层级不宜过深,避免意外操作大量数据
- 使用事务:复杂的级联操作建议使用事务包装
typescript
// 使用事务确保数据一致性
orm.beginTransaction();
try {
orm.insertWithRelations(author);
orm.commit();
} catch (e) {
orm.rollback();
throw e;
}- 日志记录:重要数据操作前建议记录日志
- 软删除优先:对于可能需要恢复的数据,优先使用软删除