반응형
라이프사이클을 같이 관리하고 싶다면 cascade 옵션을 ["insert","update","delete"] 로 지정한다.
(부모가 하나일때!)
onDelete는 @ManyToOne 쪽에 설정한다. (자식이 부모따라 가는 매커니즘)
# 1대1 관계
일대일 관계는 A가 B의 인스턴스를 하나만 포함하고 B가 A의 인스턴스를 하나만 포함하는 관계입니다.
예를 들어 사용자 및 프로필 엔터티를 살펴보겠습니다.
사용자는 단일 프로필만 가질 수 있으며 단일 프로필은 단일 사용자만 소유합니다.
FK 쪽에만 관계 지정하기
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column()
gender: string;
@Column()
photo: string;
}
// @JoinColumn을 설정한 쪽의 테이블에는 대상 엔터티 테이블에 대한 "관계 ID"와 외래 키가 포함됩니다.
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;
}
1대1 관계 저장하기
const profile = new Profile();
profile.gender = "male";
profile.photo = "me.jpg";
// cascade 설정이 되어있으면 profile은 저장하지 않아도 됨.
await connection.manager.save(profile);
const user = new User();
user.name = 'Joe Smith';
user.profile = profile;
// cascade 설정이 되어있으면 profile은 저장하지 않아도 됨.
await connection.manager.save(user);
1대1 관계 불러오기
// eager 없으면 relation 옵션 줘야 함
const userRepository = connection.getRepository(User);
const users = await userRepository.find({ relations: ["profile"] });
// 쿼리빌더
const users = await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.getMany();
양방향 참조 지정
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;
}
// ** @JoinColumn은 관계의 한쪽에만 있어야 합니다. 즉, 외래 키를 소유할 테이블에 있어야 합니다. **
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;
}
// 역방향 조인이 가능하다.
const profiles = await connection
.getRepository(Profile)
.createQueryBuilder("profile")
.leftJoinAndSelect("profile.user", "user")
.getMany();
// 근데 꼭 릴레이션 설정이 되어있어야 조인할수 있는건 아님.
const user = await createQueryBuilder("user")
.leftJoinAndSelect(Photo, "photo", "photo.userId = user.id")
.getMany();
# 다대일 , 일대다
다대일/일대다 관계는 A가 B의 여러 인스턴스를 포함하지만 B는 A의 인스턴스를 하나만 포함하는 관계입니다.
예를 들어 User 및 Photo 엔터티를 살펴보겠습니다.
사용자는 여러 장의 사진을 가질 수 있지만 각 사진은 한 명의 사용자만 소유합니다.
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
import {User} from "./User";
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column()
url: string;
// 사진은 여러개 있을 수 있지만 사용자는 반드시 하나.
// 사용자의 user.photos필드에 Photo[]
// @JoinColumn({ referencedColumnName: "id" ,name:userId})가 디폴트임
@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;
// 사진은 여러개 있을 수 있지만 사용자는 반드시 하나.
// photo의 photo.user 필드에 User
// @JoinColumn({ referencedColumnName: "id" ,name:photoId})가 디폴트임
@OneToMany(() => Photo, photo => photo.user)
photos: Photo[];
}
- @ManyToOne / @OneToMany 관계에서는 @JoinColumn을 생략할 수 있습니다.
- @ManyToOne을 설정한 엔터티에 FK와 relationId가 생성됩니다.
- @OneToMany를 사용하려면 @ManyToOne이 필요합니다.
- @ManyToOne 관계에만 관심이 있는 경우 @OneToMany 설정은 생략해도 됨
해당 관계 저장 방법
// 사진을 먼저 저장하고 부모에 참조관계 만들어 저장.
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];
// cascade 옵션 : from 부모 to 자식. 부모 하나만 저장하면 됨.
await connection.manager.save(user);
// 사용자 만들고 photo에 참조관계 만들어 저장
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);
// 쿼리하기
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"] });
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();
# 다대다 관계
사실 다대다 관계는 테이블로 분리할 수 있다.
김영한님 께서 말씀하시길...
- 편리해 보이지만 실무에서 사용하면 안된다.
- 개발하다 보면, 연결 테이블이 단순히 연결만 하고 끝나지 않는다. 조인 테이블 자체에 주문시간, 수량 같은 추가 데이터가 많이 들어갈 수 있다.
- 하지만, 매핑 정보만 넣는 것이 가능하고, 추가 정보를 넣는 것 자체가 불가능하다.
- 그리고 중간 테이블이 숨겨져 있기 때문에 예상하지 못하는 쿼리들이 나간다.
- 이런 문제점들 때문에 실무에서는 안쓰는게 맞다고 본다.
출처: https://ict-nroo.tistory.com/127 [개발자의 기록습관]
나중에 추가 학습하도록 한다 (TBD)
반응형
'FrontEnd' 카테고리의 다른 글
[Epic React] React.createElement, jsx와 바벨 (0) | 2021.11.30 |
---|---|
[Epic React] Dom Api로 비동기 렌더링 (0) | 2021.11.30 |
[TypeORM] 릴레이션 기본과 조인 (0) | 2021.11.01 |
TypeORM 스터디 : QueryBuilder 3편 - 캐싱 (0) | 2021.10.30 |
TypeORM 스터디 : QueryBuilder 2편 - CRUD 심화 (0) | 2021.10.30 |