TypeORM Relations
2020. 10. 4. 14:05ㆍDEV/TypeORM
반응형
Relations
관계형 Entity 설계
- 1:1 : @OneToOne
- N:1 : @ManyToOne
- 1:N : @OneToMany
- N:N : @ManyToMany
One-To-One
서로 1:1로만 매칭이 되는 모델에 사용
관계 설정
@OneToOne
- Target Type을 지정하여 연결 설정
- 단방향/양방향 설정 가능
- 단방향 : 한쪽에만 설정
- 양방향 : 양쪽에 설정
@JoinColumn
- 관계 중 Foreign Key가 있는 쪽에 설정 필수
- 설정된 테이블에 "relation id"와 foreign key가 저장됨
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(type => Profile)
@JoinColumn()
profile: Profile;
}
생성된 데이터
+-------------+--------------+----------------------------+
| profile |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| gender | varchar(255) | |
| photo | varchar(255) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| profileId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
호출 방법
Save
한 번만 호출해서 저장 가능
const profile = new Profile();
profile.gender = "male";
profile.photo = "me.jpg";
await connection.manager.save(profile);
const user = new User();
user.name = 'Joe Smith';
user.profile = profile;
await connection.manager.save(user);
find
정보를 join해서 호출
특정 FindOptions에 특정 관계 설정 필요
const userRepository = connection.getRepository(User);
const users = await userRepository.find({ relations: ["profile"] });
또는, QueryBuilder로 Join
const users = await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.getMany();
Many-To-One / One-To-Many
- 1:N, N:1 등의 관계 설정
- @OneToMany와 @ManyToOne은 반드시 함께 설정
- @JoinColumn 생략 가능 > @ManyToOne에 Foriegn Key 생성
설정 방법
예를 들어 User(1): Photo(N)을 설정
- User : @OneToMany
- Photo : @ManyToOne
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;
}
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[];
}
생성된 데이터
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| url | varchar(255) | |
| userId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
사용 방법
save
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.name = "John";
user.photos = [photo1, photo2];
await connection.manager.save(user);
또는
const user = new User();
user.name = "Leo";
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);
find
@findOptions로 관계호출
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"] });
QueryBuilder로 Join
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();
Many-To-Many [WIP]
서로가 N:N으로 연결되는 Category와 Question의 경우
관계 설정
@ManyToMany
- Owining side에 설정
@JoinTable
- @ManyToMany 필수
- Owining side에 설정
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
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[];
}
생성된 데이터
+-------------+--------------+----------------------------+
| category |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| question |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| title | varchar(255) | |
| text | varchar(255) | |
+-------------+--------------+----------------------------+
+-------------+--------------+----------------------------+
| question_categories_category |
+-------------+--------------+----------------------------+
| questionId | int(11) | PRIMARY KEY FOREIGN KEY |
| categoryId | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
사용 방법 [WIP]
save
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.title = "dogs";
question.text = "who let the dogs out?";
question.categories = [category1, category2];
await connection.manager.save(question);
delete realationship
- 관계를 삭제한 후 저장하는 방식
- JoinTable의 관계만 제거 되고 각 테이블의 데이터는 보존됨
const question = getRepository(Question);
question.categories = question.categories.filter(category => {
category.id !== categoryToRemove.id
})
await connection.manager.save(question)
Relation Options
- eager: boolean - If set to true, the relation will always be loaded with the main entity when using find* methods or QueryBuilder on this entity
- cascade: boolean | ("insert" | "update")[] - If set to true, the related object will be inserted and updated in the database. You can also specify an array of cascade options.
- onDelete: "RESTRICT"|"CASCADE"|"SET NULL" - specifies how foreign key should behave when referenced object is deleted
- primary: boolean - Indicates whether this relation's column will be a primary column or not.
- nullable: boolean - Indicates whether this relation's column is nullable or not. By default it is nullable.
Cascade
- cascade 설정하면 둘 사이 영속 관계가 설정됨
- eg. One Side 삭제 시 Many Side도 삭제됨
@Entity()
class Book extends BaseEntity {
@ManyToOne(() => Author, (author) => author.books, {
onDelete: 'CASCADE',
})
public author?: Author
}
@Entity()
class Author extends BaseEntity {
@OneToMany(() => Book, (book) => book.author, {
cascade: true,
})
public books: Book[];
}
- Author 삭제 시 Book도 삭제됨
- onDelete sets the authorId foreign key to CASCADE onDelete on Book. This means that when the author is deleted, the book is also deleted. >> REF
@JoinColumn & Unique Key
/**
* Project의 Target Lang에 따른 Task 생성 관계 설정
* - TaskType: Translation
*/
@Column({ nullable: true })
projectId?: number
@ManyToOne((type) => ProjectEntity, (project) => project.tasks, {
onDelete: 'CASCADE',
})
/**
* @param name : 현재 Entity의 컬럼명
* @param referencedColumnName : Join 대상 Entity의 컬럼명
*/
@JoinColumn({ name: 'projectId', referencedColumnName: 'id' })
project?: ProjectEntity
---
/**
* Task Join 설정
* - Task.targetId = Project.id and Task.type = TaskType.TRANSLATION
*/
@OneToMany((type) => TaskEntity, (task) => task.project, {
cascade: true,
})
tasks: TaskEntity[]
References
반응형
'DEV > TypeORM' 카테고리의 다른 글
TypeORM Migration하기 (0) | 2020.10.06 |
---|---|
TypeORM Entity (0) | 2020.10.03 |
TypeORM Pattern (0) | 2020.10.02 |