OneToMany / ManyToOne Relations에 관한 내용을 번역과 동시에 제 생각을 넣은 글입니다. 더 정확한 사실을 알고 싶다면 해당 링크를 방문해 주세요. 번역 시작하겠습니다.
A엔티티와 B엔티티를 갖고 있는 상태로 가정하겠습니다.
Many-to-one과 One-to-many는 A엔티티는 B의 여러 인스턴스를 갖고 있는 관계입니다. 반대로 B는 A 엔티티의 하나의 인스턴스를 갖고 있습니다. 즉, A는 B의 여러 인스턴스 그리고 B는 오직 하나의 A 인스턴스를 갖고 있는 것을 의미합니다.
이제 늘 그렇듯 예제를 살펴보도록 하겠습니다. 예시는 A는 User Entity, B는 Photo Entity로 정의하고 시작하겠습니다. 다시 말씀드리자면, 위의 내용을 토대로 User는 여러개의 Photo를 가질 수 있지만, Photo는 단 하나의 User만 갖고 있는 것입니다.
Photo Entity
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
}
User Entity
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[]
}
위 예시를 보자면 User Entity에 @OneToMany를 추가했고 관계 타입을 명시해두었습니다.
@OneToMany(() => Photo, (photo) => photo.user) 이 부분을 뜻하는 것입니다. 그리고 OneToOne과 달라진 부분은 우리는 @JoinColumn을 생략할 수 있다는 것입니다. 언제? OneToMany와 ManyToOne 관계에서 말이죠? 그리고 @OneToMany는 @ManyToOne없이는 존재할 수 없습니다. 만약 @OneToMany를 사용하고 싶다면, @ManyToOne은 필수요건중에 하나라고 말씀드릴 수 있습니다.
그러나 반대는 그렇지 않습니다. 만약 @ManyToOne 관계를 갖고싶다면? 우리는 관계된 엔티티에 @OneToMany 없이 정의할 순 있습니다. 그리고 @ManyToOne을 지정해둔 Entity에 realation id와 외래키(FK)가 설정됩니다.
아래는 현재 테이블의 상태를 보여줍니다.
+-------------+--------------+----------------------------+
| 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) | |
+-------------+--------------+----------------------------+
Photo에 @ManyToOne을 지정해놓았기 때문에 외래키가 설정된것이 보입니다.
이제 여기서부터는 저장에 관해서 다루어볼 것입니다. 저장은 save 메소드를 사용합니다.
const photo1 = new Photo()
photo1.url = "me.jpg"
await dataSource.manager.save(photo1)
const photo2 = new Photo()
photo2.url = "me-and-bears.jpg"
await dataSource.manager.save(photo2)
const user = new User()
user.name = "John"
user.photos = [photo1, photo2]
await dataSource.manager.save(user)
또 다른 대안으로는 아래와 같은 코드 작성이 가능합니다.
const user = new User()
user.name = "Leo"
await dataSource.manager.save(user)
const photo1 = new Photo()
photo1.url = "me.jpg"
photo1.user = user
await dataSource.manager.save(photo1)
const photo2 = new Photo()
photo2.url = "me-and-bears.jpg"
photo2.user = user
await dataSource.manager.save(photo2)
Cascades를 설정해둠으로써, 우리는 단 한번의 save 를 사용해 이 관계를 저장할 수 있습니다. 제 생각입니다. OneToOne처럼 외래키가 존재하지 않는 것부터 저장을 한 후에는 자동으로 한번에 저장이 가능한 것을 의미하는 것 같습니다.
Find 옵션에 대해서 다루어보겠습니다.
Find 옵션에서는 관계를 분명히 명시해야합니다. 여러개 Photo와 함께 User를 불러오기 위해서는 말이에요.
const userRepository = dataSource.getRepository(User)
const users = await userRepository.find({
relations: {
photos: true,
},
})
// or from inverse side
const photoRepository = dataSource.getRepository(Photo)
const photos = await photoRepository.find({
relations: {
user: true,
},
})
또는 QueryBuilder를 사용해서 불러올 수도 있습니다.
const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.getMany()
// or from inverse side
const photos = await dataSource
.getRepository(Photo)
.createQueryBuilder("photo")
.leftJoinAndSelect("photo.user", "user")
.getMany()
여기서 주의해야될 점은, 관계에 eager loading을 설정해놓는다면, 우리는 관계를 불러올때 명시할 필요가 없이 항상 자동으로 같이 불러오게 될 것입니다. 근데 만약 우리가 QueryBuilder를 사용한다면 그렇지 않다는 것입니다. 뭐냐 그렇지 않냐면 자동으로 같이 불러오지 않기 때문에 무조건 비활성화 된다고 생각하시면 됩니다. 그래서 관계를 명시적으로 로드해야합니다.
즉 아래와 같이 정리할 수 있습니다.
Eager Loading: find 명령어를 사용할 때 관계를 자동으로 로드합니다. QueryBuilder에서는 비활성화됩니다.
QueryBuilder: 관계를 로드하려면 leftJoinAndSelect를 사용하여 명시적으로 관계를 지정해야 합니다.
이상 끝.
'Project > TypeORM' 카테고리의 다른 글
[번역] ManyToMany Relations (0) | 2024.08.23 |
---|---|
[번역] @OneToOne Relations (0) | 2024.08.19 |
어떻게 데이터를 건드릴 수 있을까? (0) | 2024.08.14 |
왜 ORM을 사용하려고 하는데요? (0) | 2024.08.14 |
너랑 나랑 무슨 관계(relation)야? (0) | 2024.08.13 |