6月2日 23:06

TypeORM 实体关系怎么定义?一对一、一对多、多对多装饰器详解

TypeORM 用装饰器定义实体间的关系:一对一、一对多、多对多。关键是搞清谁是"拥有方"(存外键的一方),谁是"反方"(只声明关系不存外键)。

一对一(OneToOne)

一个人只有一个档案,一个档案只属于一个人:

typescript
@Entity() export class User { @PrimaryGeneratedColumn() id: number; @OneToOne(() => Profile, profile => profile.user) @JoinColumn() // 外键加在这一侧(拥有方) profile: Profile; } @Entity() export class Profile { @PrimaryGeneratedColumn() id: number; @OneToOne(() => User, user => user.profile) user: User; // 反方,不加 @JoinColumn }

@JoinColumn() 标记拥有方——数据库会在 User 表加一个 profileId 外键列。一对一关系只有一方能加 @JoinColumn,另一方是反向引用。

一对多 / 多对一(OneToMany / ManyToOne)

一个用户有多篇文章,一篇文章属于一个用户:

typescript
@Entity() export class User { @PrimaryGeneratedColumn() id: number; @OneToMany(() => Post, post => post.author) posts: Post[]; } @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @ManyToOne(() => User, user => user.posts) @JoinColumn({ name: 'authorId' }) // 外键在 Post 表 author: User; }

ManyToOne 总是拥有方——外键存在"多"的一侧的表里。OneToMany 只是反向引用,不能单独存在,必须配对 ManyToOne。

数据库里 Post 表有一个 authorId 列指向 User.id。

多对多(ManyToMany)

一个文章有多个标签,一个标签属于多篇文章:

typescript
@Entity() export class Post { @PrimaryGeneratedColumn() id: number; @ManyToMany(() => Tag, tag => tag.posts) @JoinTable() // 拥有方加 @JoinTable,自动创建中间表 tags: Tag[]; } @Entity() export class Tag { @PrimaryGeneratedColumn() id: number; @ManyToMany(() => Post, post => post.tags) posts: Post[]; // 反方,不加 @JoinTable }

@JoinTable() 让 TypeORM 自动创建中间表 post_tags_tags(post_id + tag_id)。只有一方加 @JoinTable,另一方是反向引用。

加载关联数据

定义了关系不代表查询时自动加载。必须显式指定:

typescript
// 方式一:find 时指定 relations const user = await userRepo.findOne({ where: { id: 1 }, relations: ['posts', 'posts.tags'] // 支持嵌套 }); // 方式二:QueryBuilder join const user = await userRepo .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .leftJoinAndSelect('post.tags', 'tag') .where('user.id = :id', { id: 1 }) .getOne();

不加 relationsleftJoinAndSelect,返回的 user.posts 就是 undefined。这是 TypeORM 的设计选择——避免无意义的 JOIN 查询。

常见坑

循环引用:User 引用 Post,Post 引用 User。TypeScript 用 () => Post 延迟求值避免循环依赖。不要直接写 Post,必须用箭头函数。

级联操作cascade: true 让保存 User 时自动保存关联的 Post。小心使用——可能在你不知情时写入大量数据。建议只在明确的父子关系上使用。

删除行为onDelete: 'CASCADE' 在数据库层面级联删除。删除 User 时自动删除其所有 Post。比应用层级联更可靠。

标签:TypeORM