mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
added some relations docs
This commit is contained in:
parent
0884335a2d
commit
576d0db4cd
@ -1,16 +1,15 @@
|
||||
# Relations
|
||||
|
||||
* What are relations
|
||||
* One-to-one relation
|
||||
* One-to-one relations
|
||||
* Many-to-one / one-to-many relations
|
||||
* Many-to-many relation
|
||||
* Many-to-many relations
|
||||
* Having both relation and relation column
|
||||
* How to load relations in entities
|
||||
* Self referencing
|
||||
* Relation options
|
||||
* Lazy relations
|
||||
* Self referencing
|
||||
* Eager relations
|
||||
* Usage examples
|
||||
* Lazy relations
|
||||
|
||||
## What are relations
|
||||
|
||||
@ -22,10 +21,11 @@ There are several types of relations:
|
||||
* one-to-many using `@OneToMany` decorator
|
||||
* many-to-many using `@ManyToMany` decorator
|
||||
|
||||
## One-to-one relation
|
||||
## One-to-one relations
|
||||
|
||||
Let's create an example of one-to-one relation between `User` and `Profile`.
|
||||
User can have a single profile and single profile can be one for a single user.
|
||||
One-to-one is a relation where A contains only once instance of B, and B contains only one instance of A.
|
||||
Let's take for example `User` and `Profile` entities.
|
||||
User can have only a single profile, and single profile is owned only by a single user.
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
|
||||
@ -47,6 +47,7 @@ export class Profile {
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn} from "typeorm";
|
||||
import {Profile} from "./Profile";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@ -67,7 +68,6 @@ export class User {
|
||||
Here we added `@OneToOne` decorator to the `profile` property and specified target relation type `Profile` to it.
|
||||
We also added `@JoinColumn` decorator which is required and must be set only on one side of relation.
|
||||
On which side you set `@JoinColumn` that side's table will contain "relation id" and foreign keys to target entity table.
|
||||
In our example we had one-side-only relation.
|
||||
|
||||
This example will produce following tables:
|
||||
|
||||
@ -101,7 +101,7 @@ await connection.manager.save(profile);
|
||||
|
||||
const user = new User();
|
||||
user.profile = profile;
|
||||
await connection.manager.save(profile);
|
||||
await connection.manager.save(user);
|
||||
```
|
||||
|
||||
With cascades enabled you can save this relation with only one `save` call.
|
||||
@ -132,7 +132,8 @@ Bi-directional are relations with decorators on both sides of a relation.
|
||||
We just created a uni-directional relation. Let's make it bi-directional:
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
|
||||
import {Entity, PrimaryGeneratedColumn, Column, OneToOne} from "typeorm";
|
||||
import {User} from "./User";
|
||||
|
||||
@Entity()
|
||||
export class Profile {
|
||||
@ -154,6 +155,7 @@ export class Profile {
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn} from "typeorm";
|
||||
import {Profile} from "./Profile";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@ -172,7 +174,7 @@ export class User {
|
||||
```
|
||||
|
||||
We just made our relation bi-directional. Note, inverse relation does not have a `@JoinColumn` decorator.
|
||||
As we already told you `@JoinColumn` must be only on one side of the relation - which table will own a foreign key.
|
||||
`@JoinColumn` must be only on one side of the relation - which table will own a foreign key.
|
||||
|
||||
Bi-directional relations allow you to join relations from both sides using `QueryBuilder`:
|
||||
|
||||
@ -184,4 +186,310 @@ const profiles = await connection
|
||||
.getMany();
|
||||
```
|
||||
|
||||
## Many-to-one / one-to-many relations
|
||||
## Many-to-one / one-to-many relations
|
||||
|
||||
Many-to-one / one-to-many is a relation where A contains multiple instances of B, but B contains only one instance of A.
|
||||
Let's take for example `User` and `Photo` entities.
|
||||
User can have multiple photos, but each photo is owned only by a single user.
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
|
||||
import {User} from "./User";
|
||||
|
||||
@Entity()
|
||||
export class Photo {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@ManyToOne(type => User, user => user.photos)
|
||||
user: User;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm";
|
||||
import {Photo} from "./Photo";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToMany(type => Photo, photo => photo.user)
|
||||
photos: Photo[];
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Here we added `@ManyToOne` decorator to the `photos` property and specified target relation type `Photo` to it.
|
||||
You can omit `@JoinColumn` decorator in case of `@ManyToOne` / `@OneToMany` relation.
|
||||
`@OneToMany` decorator cannot exist without `@ManyToOne` decorator.
|
||||
If you want to use `@OneToMany` decorator `@ManyToOne` is required.
|
||||
Where you set `@ManyToOne` decorator - its related entity will have "relation id" and foreign key.
|
||||
|
||||
This example will produce following tables:
|
||||
|
||||
```shell
|
||||
+-------------+--------------+----------------------------+
|
||||
| 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) | |
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
Example how to save such relation:
|
||||
|
||||
```typescript
|
||||
const photo1 = new Photo();
|
||||
photo1.url = "me.jpg";
|
||||
await connection.manager.save(photo1);
|
||||
|
||||
const photo2 = new Photo();
|
||||
photo2.url = "me-and-bears.jpg";
|
||||
await connection.manager.save(photo2);
|
||||
|
||||
const user = new User();
|
||||
user.photos = [photo1, photo2];
|
||||
await connection.manager.save(user);
|
||||
```
|
||||
|
||||
or alternative you can do:
|
||||
|
||||
```typescript
|
||||
const user = new User();
|
||||
await connection.manager.save(user);
|
||||
|
||||
const photo1 = new Photo();
|
||||
photo1.url = "me.jpg";
|
||||
photo1.user = user;
|
||||
await connection.manager.save(photo1);
|
||||
|
||||
const photo2 = new Photo();
|
||||
photo2.url = "me-and-bears.jpg";
|
||||
photo2.user = user;
|
||||
await connection.manager.save(photo2);
|
||||
```
|
||||
|
||||
With cascades enabled you can save this relation with only one `save` call.
|
||||
|
||||
To load user with photos inside you must specify relation in `FindOptions`:
|
||||
|
||||
```typescript
|
||||
const userRepository = connection.getRepository(User);
|
||||
const users = await userRepository.find({ relations: ["photos"] });
|
||||
|
||||
// or from inverse side
|
||||
|
||||
const photoRepository = connection.getRepository(Photo);
|
||||
const photos = await photoRepository.find({ relations: ["user"] });
|
||||
```
|
||||
|
||||
Or using `QueryBuilder` you can join them:
|
||||
|
||||
```typescript
|
||||
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();
|
||||
```
|
||||
|
||||
With eager loading enabled on a relation you don't have to specify relation or join it - it will be loaded automatically ALWAYS.
|
||||
|
||||
## Many-to-many relations
|
||||
|
||||
Many-to-many is a relation where A contains multiple instances of B, and B contain multiple instances of A.
|
||||
Let's take for example `Question` and `Category` entities.
|
||||
Question can have multiple categories, and each category can have multiple questions.
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
|
||||
|
||||
@Entity()
|
||||
export class Category {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";
|
||||
import {Category} from "./Category";
|
||||
|
||||
@Entity()
|
||||
export class Question {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@ManyToMany(type => Category)
|
||||
@JoinTable()
|
||||
categories: Category[];
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
`@JoinTable()` decorator is required for `@ManyToMany` relations.
|
||||
You must put `@JoinTable` decorator only from one (owning) side of relation.
|
||||
|
||||
This example will produce following tables:
|
||||
|
||||
```shell
|
||||
+-------------+--------------+----------------------------+
|
||||
| category |
|
||||
+-------------+--------------+----------------------------+
|
||||
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
|
||||
| name | varchar(255) | |
|
||||
+-------------+--------------+----------------------------+
|
||||
|
||||
+-------------+--------------+----------------------------+
|
||||
| question |
|
||||
+-------------+--------------+----------------------------+
|
||||
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
|
||||
| title | varchar(255) | |
|
||||
+-------------+--------------+----------------------------+
|
||||
|
||||
+-------------+--------------+----------------------------+
|
||||
| question_categories |
|
||||
+-------------+--------------+----------------------------+
|
||||
| questionId | int(11) | PRIMARY KEY FOREIGN KEY |
|
||||
| categoryId | int(11) | PRIMARY KEY FOREIGN KEY |
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
Example how to save such relation:
|
||||
|
||||
```typescript
|
||||
const category1 = new Category();
|
||||
category1.name = "animals";
|
||||
await connection.manager.save(category1);
|
||||
|
||||
const category2 = new Category();
|
||||
category2.name = "zoo";
|
||||
await connection.manager.save(category2);
|
||||
|
||||
const question = new Question();
|
||||
question.categories = [category1, category2];
|
||||
await connection.manager.save(question);
|
||||
```
|
||||
|
||||
With cascades enabled you can save this relation with only one `save` call.
|
||||
|
||||
To load question with categories inside you must specify relation in `FindOptions`:
|
||||
|
||||
```typescript
|
||||
const questionRepository = connection.getRepository(Question);
|
||||
const questions = await questionRepository.find({ relations: ["categories"] });
|
||||
```
|
||||
|
||||
Or using `QueryBuilder` you can join them:
|
||||
|
||||
```typescript
|
||||
const questions = await connection
|
||||
.getRepository(Question)
|
||||
.createQueryBuilder("question")
|
||||
.leftJoinAndSelect("question.categories", "category")
|
||||
.getMany();
|
||||
```
|
||||
|
||||
With eager loading enabled on a relation you don't have to specify relation or join it - it will be loaded automatically ALWAYS.
|
||||
|
||||
Relations can be uni-directional and bi-directional.
|
||||
Uni-directional are relations with relation decorator only on one side.
|
||||
Bi-directional are relations with decorators on both sides of a relation.
|
||||
|
||||
We just created a uni-directional relation. Let's make it bi-directional:
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany} from "typeorm";
|
||||
import {Question} from "./Question";
|
||||
|
||||
@Entity()
|
||||
export class Category {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Question, question => question.categories)
|
||||
questions: Question[];
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";
|
||||
import {Category} from "./Category";
|
||||
|
||||
@Entity()
|
||||
export class Question {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@ManyToMany(type => Category, category => category.questions)
|
||||
@JoinTable()
|
||||
categories: Category[];
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
We just made our relation bi-directional. Note, inverse relation does not have a `@JoinTable` decorator.
|
||||
`@JoinTable` must be only on one side of the relation - which table will own a foreign key.
|
||||
|
||||
Bi-directional relations allow you to join relations from both sides using `QueryBuilder`:
|
||||
|
||||
```typescript
|
||||
const categoriesWithQuestions = await connection
|
||||
.getRepository(Category)
|
||||
.createQueryBuilder("category")
|
||||
.leftJoinAndSelect("category.questions", "question")
|
||||
.getMany();
|
||||
```
|
||||
|
||||
## Having both relation and relation column
|
||||
Loading…
x
Reference in New Issue
Block a user