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();
不加 relations 或 leftJoinAndSelect,返回的 user.posts 就是 undefined。这是 TypeORM 的设计选择——避免无意义的 JOIN 查询。
常见坑
循环引用:User 引用 Post,Post 引用 User。TypeScript 用 () => Post 延迟求值避免循环依赖。不要直接写 Post,必须用箭头函数。
级联操作:cascade: true 让保存 User 时自动保存关联的 Post。小心使用——可能在你不知情时写入大量数据。建议只在明确的父子关系上使用。
删除行为:onDelete: 'CASCADE' 在数据库层面级联删除。删除 User 时自动删除其所有 Post。比应用层级联更可靠。