下面设计许多装饰器,可以先熟悉一下,更好的理解下面的关系
点击跳转装饰器
在 TypeORM 中,一般使用使用装饰器来定义实体之间的关系
要想连接两个实体,可以在表中指定一个列来明确他们之间的关系,这个列叫做外键列
@JoinColumn() - 用来指定关系的外键列
通过指定外键列,可以在关系中明确指定哪个列用于连接两个实体,从而更加精细地控制实体之间的关系。
外键列是一个用于连接两个实体的列,它存储了另一个实体的主键或唯一标识符。
设置@JoinColumn的哪一方,哪一方的表将包含一个"relation id"和目标实体表的外键。
// 实体
@Entity()
class User {
// 主键
@PrimaryGeneratedColumn()
id: number;
// 普通键
@Column()
name: string;
// 定义一对一关系
@OneToOne(type => Profile)
// 指定关系的外键列
@JoinColumn()
profile: Profile;
}
@Entity()
class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column()
age: number;
// 指向 user 中的 profile 列,表示两个实体之间的关系是双向的
@OneToOne(type => User, user => user.profile)
user: User;
}
外键列名默认为${propertyName}_id,即profile_id,可以通过在 @JoinColumn() 装饰器中传递参数来指定外键列名。
@Entity()
class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToOne(type => Profile)
@JoinColumn({ name: "my_profile_id" })
profile: Profile;
}
使用 @JoinColumn({ name: "my_profile_id" }) 来指定外键列名为 my_profile_id,而不是默认的 profile_id。
Join 列始终是对其他一些列的引用,默认情况下,关系始终引用相关实体的主列,但是也可以指定引用其他列,@JoinColumn对象参数包含一个 referencedColumnName 属性,用于指定引用列的名称,例如:
@Entity()
class Product {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
// 多个 `Product` 实体 对应一个 `Category`实体
@ManyToOne(type => Category)
// 使用 `referencedColumnName` 属性来指定引用列的名称为 `name`
@JoinColumn({ referencedColumnName: "name" })
category: Category;
}
@Entity()
class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
// // 一个 `Category` 实体 拥有多个 `Product`实体
@OneToMany(type => Product, product => product.category)
// 因为拥有多个 `Product`实体,所以存储的是数组
products: Product[];
}
@JoinTable - 创建一个联结表,引用相关实体
@JoinTable() 装饰器用于定义多对多关系的联结表。联结表是一个中间表,用于存储多对多关系中实体之间的关联关系。
在 @JoinTable() 装饰器中,可以传递一个对象参数,该对象参数包含一个 name 属性,用于指定联结表的表名,以及 joinColumn 和 inverseJoinColumn 属性,用于指定联结表中的外键列。
@Entity()
class Question {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToMany(type => Category)
// 联结表
@JoinTable({
// 设置表名
name: "question_categories",
// 指定联结表中的外键列
joinColumn: {
name: "question",
referencedColumnName: "id"
},
inverseJoinColumn: {
name: "category",
referencedColumnName: "id"
}
})
categories: Category[];
}
@Entity()
class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Question, question => question.categories)
questions: Question[];
}
joinColumn属性指定了question列为联结表中的外键列,引用了Question实体的id列
inverseJoinColumn属性指定了category列为联结表中的外键列,引用了Category实体的id列。
一对一关系
@OneToOne():定义一对一关系
一对一关系:指两个实体之间的关系是唯一的。例如,一个人只有一个身份证号,一个身份证号也只属于一个人。
例如:
关系可以是单向的和双向的,两侧都有@OneToOne才是双向,一侧有是单向
import { Entity, PrimaryGeneratedColumn, Column, OneToOne } from "typeorm";
import { User } from "./User";
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column()
gender: string;
@Column()
photo: string;
@OneToOne(() => User, user => user.profile) // 将另一面指定为第二个参数
user: User;
}
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm";
import { Profile } from "./Profile";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToOne(() => Profile, profile => profile.user) // 指定另一面作为第二个参数
@JoinColumn()
profile: Profile;
}
注意:
@JoinColumn必须只在关系的一侧且拥有外键的表上。拥有外键:就是@Column()普通键,不能只有主键
// 使用
const profiles = await connection
.getRepository(Profile)
.createQueryBuilder("profile")
.leftJoinAndSelect("profile.user", "user")
.getMany();
一对多关系
@OneToMany():定义一对多关系
@ManyToOne可以单独使用,但@OneToMany必须搭配@ManyToOne使用
一对多关系:指一个实体可以对应多个另一个实体。例如,一个学校可以有多个学生,但一个学生只能在一个学校就读。
User 可以拥有多张 photos,但每张 photo 仅由一位 user 拥有
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[];
}
结果:
在你设置@ManyToOne的地方,相关实体将有"关联 id"和外键。
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| url | varchar(255) | |
| userId | int(11) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
使用:
const users = await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.getMany();
// or from inverse side
const photos = await connection
.getRepository(Photo)
.createQueryBuilder("photo")
.leftJoinAndSelect("photo.user", "user")
.getMany();
多对一关系
@ManyToOne():定义多对一关系
多对一关系:指多个实体可以对应一个另一个实体。例如,多个学生可以在同一个学校就读。
@ManyToOne可以单独使用,但@OneToMany必须搭配@ManyToOne使用
多对多关系
@ManyToMany():定义多对多关系
@JoinTable()是@ManyToMany关系所必需的,必须把@JoinTable放在关系的一个(拥有)方面,只有一个有,ueng两个同时都存在。
多对多关系:指多个实体之间相互关联。例如,多个学生可以选择同一门课程,同一门课程也可以有多个学生选择。
单向、双向都是可以的
单向是仅在一侧与关系装饰器的关系。
双向是与关系两侧的装饰者的关系。
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm";
import { Question } from "./Question";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(() => 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;
@ManyToMany(() => Category)
@JoinTable()
categories: Category[];
}
结果:
+-------------+--------------+----------------------------+
| category |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| question |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| title | varchar(255) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| question_categories_category |
+-------------+--------------+----------------------------+
| questionId | int(11) | PRIMARY KEY FOREIGN KEY |
| categoryId | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
使用:
const categoriesWithQuestions = await connection
.getRepository(Category)
.createQueryBuilder("category")
.leftJoinAndSelect("category.questions", "question")
.getMany();