关系
什么是关系
关系帮助您轻松处理相关实体。 有几种类型的关系:
关系选项
您可以为关系指定几个选项:
eager: boolean
- 如果设置为true,则在使用find*
方法或对此实体使用QueryBuilder
时,关系将始终与主实体一起加载cascade: boolean | ("insert" | "update")[]
- 如果设置为true,则相关对象将被插入和更新到数据库中。您还可以指定级联选项的数组。onDelete: "RESTRICT"|"CASCADE"|"SET NULL"
- 当引用的对象被删除时,指定外键应如何行为nullable: boolean
- 指示此关系的列是否可为空。默认情况下,它是可为空的。orphanedRowAction: "nullify" | "delete" | "soft-delete" | disable
- 当父实体被保存(启用级联操作)时,如果某些仍存在于数据库中的子对象没有附属,则此选项控制对它们的处理。 delete 将从数据库中删除这些子对象。 soft-delete 将子对象标记为软删除。 nullify 将删除关系键。 disable 将保持关系不变。要删除,必须使用自己的存储库。
级联操作
级联操作示例:
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm"
import { Question } from "./Question"
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@ManyToMany((type) => Question, (question) => question.categories)
questions: Question[]
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
JoinTable,
} from "typeorm"
import { Category } from "./Category"
@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column()
text: string
@ManyToMany((type) => Category, (category) => category.questions, {
cascade: true,
})
@JoinTable()
categories: Category[]
}
const category1 = new Category()
category1.name = "ORMs"
const category2 = new Category()
category2.name = "编程"
const question = new Question()
question.title = "如何提问?"
question.text = "在哪里可以提出与TypeORM相关的问题?"
question.categories = [category1, category2]
await dataSource.manager.save(question)
如您所见,在此示例中,我们没有调用 save
来保存 category1
和 category2
。
它们将自动插入,因为我们将 cascade
设置为 true
。
请记住,大权独揽也伴随着巨大的责任。 级联操作可能看起来是处理关系的好方法, 但在某些不希望的对象被保存到数据库中时,它们可能会带来错误和安全问题。 此外,它们提供了一种不太明确的将新对象保存到数据库中的方法。
级联选项
cascade
选项可以设置为 boolean
或级联选项的数组 ("insert" | "update" | "remove" | "soft-remove" | "recover")[]
。
它的默认值为 false
,表示没有级联操作。将 cascade: true
设置为启用完全级联操作。您还可以通过提供数组来指定选项。
例如:
@Entity(Post)
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column()
text: string
// 对 categories 的完全级联操作。
@ManyToMany((type) => PostCategory, {
cascade: true,
})
@JoinTable()
categories: PostCategory[]
// 这里的级联插入意味着如果在此关系上设置了新的 PostDetails 实例,
// 则在保存此 Post 实体时,它将自动插入到数据库中。
@ManyToMany((type) => PostDetails, (details) => details.posts, {
cascade: ["insert"],
})
@JoinTable()
details: PostDetails[]
// 这里的级联更新意味着如果对现有的 PostImage 进行更改,
// 在保存此 Post 实体时,它将自动更新到数据库中。
@ManyToMany((type) => PostImage, (image) => image.posts, {
cascade: ["update"],
})
@JoinTable()
images: PostImage[]
// 这里的级联插入和更新意味着如果有新的 PostInformation 实例
// 或对现有实例进行更新,将在保存此 Post 实体时自动插入或更新它们。
@ManyToMany((type) => PostInformation, (information) => information.posts, {
cascade: ["insert", "update"],
})
@JoinTable()
informations: PostInformation[]
}
@JoinColumn
选项
@JoinColumn
不仅定义了关系中包含具有外键的连接列的一侧,
还允许您自定义连接列的名称和引用列的名称。
当我们设置 @JoinColumn
时,它会自动在数据库中创建一个名为 propertyName +referencedColumnName
的列。
例如:
@ManyToOne(type => Category)
@JoinColumn() // 此装饰器对于 @ManyToOne 是可选的,但对于 @OneToOne 是必需的
category: Category;
这段代码将在数据库中创建一个名为 categoryId
的列。
如果您想在数据库中更改此名称,您可以指定自定义的连接列名称:
@ManyToOne(type => Category)
@JoinColumn({ name: "cat_id" })
category: Category;
连接列始终是对其他列的引用(使用外键)。
默认情况下,您的关系始终引用相关实体的主列。
如果您想创建与相关实体的其他列的关系 -
您也可以在 @JoinColumn
中指定它们:
@ManyToOne(type => Category)
@JoinColumn({ referencedColumnName: "name" })
category: Category;
此关系现在引用 Category
实体的 name
属性,而不是 id
属性。
该关系的列名称将变为 categoryName
。
您还可以连接多个列。请注意,默认情况下,它们不引用相关实体的主列:您必须提供引用列的名称。
@ManyToOne(type => Category)
@JoinColumn([
{ name: "category_id", referencedColumnName: "id" },
{ name: "locale_id", referencedColumnName: "locale_id" }
])
category: Category;
@JoinTable
选项
@JoinTable
用于 多对多
关系,并描述“连接”表的连接列。
连接表是 TypeORM 自动创建的特殊单独的表,其中的列引用相关实体。
您可以使用 @JoinColumn
更改连接表中的列名及其引用的列名:
您还可以更改生成的“连接”表的名称。
@ManyToMany(type => Category)
@JoinTable({
name: "question_categories", // 此关系的连接表的表名
joinColumn: {
name: "question",
referencedColumnName: "id"
},
inverseJoinColumn: {
name: "category",
referencedColumnName: "id"
}
})
categories: Category[];
如果目标表具有复合主键,
则必须向 @JoinTable
发送属性的数组。