跳转到主要内容

关系

什么是关系

关系帮助您轻松处理相关实体。 有几种类型的关系:

关系选项

您可以为关系指定几个选项:

  • 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 来保存 category1category2。 它们将自动插入,因为我们将 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 发送属性的数组。