Перейти к основному содержанию

Отношения один-к-одному

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Отношение один-к-одному означает, что сущность A содержит только один экземпляр сущности B, а сущность B содержит только один экземпляр сущности A. Рассмотрим в качестве примера сущности User (Пользователь) и Profile (Профиль). Пользователь может иметь только один профиль, а каждый профиль принадлежит только одному пользователю.

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

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

@Column()
gender: string

@Column()
photo: string
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
} from "typeorm"
import { Profile } from "./Profile"

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

@Column()
name: string

@OneToOne(() => Profile)
@JoinColumn()
profile: Profile
}

Здесь мы добавили @OneToOne к user и указали, что целевым типом отношения является Profile. Также мы добавили @JoinColumn, который обязателен и должен быть указан только на одной стороне отношения. На стороне, где указан @JoinColumn, таблица будет содержать "идентификатор отношения" (relation id) и внешние ключи к таблице целевой сущности.

Этот пример создаст следующие таблицы:

+-------------+--------------+----------------------------+
| profile |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| gender | varchar(255) | |
| photo | varchar(255) | |
+-------------+--------------+----------------------------+

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

Ещё раз: @JoinColumn должен быть указан только на одной стороне отношения — там, где в таблице базы данных должен находиться внешний ключ.

Пример сохранения такого отношения:

const profile = new Profile()
profile.gender = "male"
profile.photo = "me.jpg"
await dataSource.manager.save(profile)

const user = new User()
user.name = "Joe Smith"
user.profile = profile
await dataSource.manager.save(user)

При включённых каскадах вы можете сохранить отношение одним вызовом save.

Для загрузки пользователя с профилем необходимо указать отношение в FindOptions:

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

Или использовать QueryBuilder для объединения:

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

При включённой жадной загрузке (eager loading) для отношения вам не нужно указывать его в команде find — оно ВСЕГДА будет загружаться автоматически. При использовании QueryBuilder жадная загрузка отключена, поэтому для загрузки отношения необходимо использовать leftJoinAndSelect.

Отношения могут быть однонаправленными (uni-directional) и двунаправленными (bi-directional). Однонаправленные отношения используют декоратор отношения только на одной стороне. Двунаправленные отношения используют декораторы на обеих сторонах.

Мы создали однонаправленное отношение. Сделаем его двунаправленным:

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) // specify inverse side as a second parameter
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) // specify inverse side as a second parameter
@JoinColumn()
profile: Profile
}

Теперь наше отношение стало двунаправленным. Обратите внимание: обратное отношение (inverse relation) не имеет @JoinColumn. @JoinColumn должен присутствовать только на одной стороне отношения — в таблице, которая будет владеть внешним ключом.

Двунаправленные отношения позволяют объединять данные с обеих сторон с помощью QueryBuilder:

const profiles = await dataSource
.getRepository(Profile)
.createQueryBuilder("profile")
.leftJoinAndSelect("profile.user", "user")
.getMany()