跳至主内容区

多对一 / 一对多关系

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

多对一/一对多关系是指:A 包含多个 B 的实例,而 B 只包含一个 A 的实例。 以 UserPhoto 实体为例: 一个用户可以拥有多张照片,但每张照片仅属于一个用户。

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
import { User } from "./User"

@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
url: string

@ManyToOne(() => User, (user) => user.photos)
user: User
}
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"
import { Photo } from "./Photo"

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@OneToMany(() => Photo, (photo) => photo.user)
photos: Photo[]
}

这里我们在 photos 属性上添加了 @OneToMany 装饰器,并将目标关系类型指定为 Photo。 在 @ManyToOne / @OneToMany 关系中可省略 @JoinColumn@OneToMany 不能脱离 @ManyToOne 单独存在。 若想使用 @OneToMany,则必须配置 @ManyToOne。但反向操作不是必须的:如果只需 @ManyToOne 关系,可不定义相关实体上的 @OneToMany。 在设置 @ManyToOne 的位置,其相关实体将拥有"关系 ID"和外键。

此示例将生成以下表结构:

+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| url | varchar(255) | |
| userId | int | FOREIGN KEY |
+-------------+--------------+----------------------------+

+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+

保存此类关系的示例:

const photo1 = new Photo()
photo1.url = "me.jpg"
await dataSource.manager.save(photo1)

const photo2 = new Photo()
photo2.url = "me-and-bears.jpg"
await dataSource.manager.save(photo2)

const user = new User()
user.name = "John"
user.photos = [photo1, photo2]
await dataSource.manager.save(user)

或采用替代方案:

const user = new User()
user.name = "Leo"
await dataSource.manager.save(user)

const photo1 = new Photo()
photo1.url = "me.jpg"
photo1.user = user
await dataSource.manager.save(photo1)

const photo2 = new Photo()
photo2.url = "me-and-bears.jpg"
photo2.user = user
await dataSource.manager.save(photo2)

启用级联功能后,仅需单次 save 调用即可保存此关系。

要加载包含照片的用户数据,必须在 FindOptions 中指定关系:

const userRepository = dataSource.getRepository(User)
const users = await userRepository.find({
relations: {
photos: true,
},
})

// or from inverse side

const photoRepository = dataSource.getRepository(Photo)
const photos = await photoRepository.find({
relations: {
user: true,
},
})

或使用 QueryBuilder 进行关联查询:

const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.getMany()

// or from inverse side

const photos = await dataSource
.getRepository(Photo)
.createQueryBuilder("photo")
.leftJoinAndSelect("photo.user", "user")
.getMany()

当关系启用即时加载(eager loading)时,无需在查找命令中显式声明关系——系统将始终自动加载。 若使用 QueryBuilder 则会禁用即时加载,此时必须通过 leftJoinAndSelect 加载关系。