조인 없이 관계 테이블의 fk 조회하기 + @RelationId
조인 없이 그걸 어떻게 해? 라는 의문이 들지만 typeORM에서 구현해 놓았으므로 사용할 수 있습니다.
예를 들어 place라는 엔티티가 user와 relationship이 형성되어있다고 가정합시다.
relationship이 설정된 후 관련 테이블을 가져오는 방식은 아래와 같이 Place 엔티티 중에서 관계성을 가진 컬럼을 클래스 메서드의 option 부분에의 realtions 값으로 넘겨주면 됩니다.
자세한 건 공식 문서 (typeorm.io/#/relations-faq/how-to-load-relations-in-entities) 를 참고하면 됩니다.
const place = await Place.findOne({ id: args.placeId }, {relations: ["user"]});
* 참고로, 복잡한 조인이 요구될 때는 QueryBuilder를 사용해야 합니다. 이 부분은 추후에 사용할 일이 생기면 다뤄보도로하겠습니다.
그런데 문제는, 관계성이 있는 테이블의 id만 필요할 때가 있는데 이 경우 위와 같이 한다면 join이 되어 전체를 다 불러오게 된다는 겁니다. 이 경우에는 typeORM에서는 아래와 같이 [관계된 컬럼id] 꼴로 적어 id만을 가져올 수 있습니다.
@Column({ nullable: true })
userId: number;
@ManyToOne((type) => User, (user) => user.places)
user: User;
너무 간략하므로 공식 문서(typeorm.io/#/relations-faq/how-to-use-relation-id-without-joining-relation)의 예시를 들어 천천히 살펴보겠습니다.
아래와 같이 User와 Profile은 1:1 관계가 설정된 엔티티입니다.
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;
}
이 때 join 없이 단순히 User 엔티티를 찾아 불러온다면 아래와 같은 인스턴스를 받게 됩니다.
User {
id: 1,
name: "Umed"
}
그러나 조인없이 User 엔티티를 불러오면서 동시에 Profile 엔티티의 식별자인 id를 불러오고 싶다면 User 부분에 Profile와 연결된 컬럼 이름인 'profile'에 'Id'를 붙인 profileId라는 이름의 컬럼을 만들어주면 됩니다.
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ nullable: true })
profileId: number;
@OneToOne(type => Profile)
@JoinColumn()
profile: Profile;
}
이제 User 인스턴스를 불러올 때면 다음과 같이 profileId를 받을 수 있습니다. 조인 없이 불러 왔으므로 추후 필요해질 때만 profileId를 이용해 Profile 테이블을 조회하면 됩니다.
User {
id: 1,
name: "Umed",
profileId: 1
}
이제 실제적으로 어떻게 다른 차이를 보이는 지 확인해봅시다. 위의 예시는 아니지만, 간단히 설명하자면 Ride는 passenger와 driver라는 컬럼으로 User 테이블과 relationship을 가지고 있습니다.
Join하는 방식으로 가져온 후 ride를 출력해보겠습니다.
const ride = await Ride.findOne({ id: args.rideId }, { relations: ["passenger", "driver"] });
if (ride) console.log(ride)
출력 결과 다음과 같이 Join된 상태를 보실 수 있습니다.
Ride {
id: 7,
... 중략
passenger: User {
id: 1,
... 중략
},
driver: null // 값이 없어 join하지도 않음
}
그러나 relationship된 부분의 fk만을 확인하기위해 relationship 옵션을 달지 않은 채로 엔티티를 다음과 같이 수정하고, 호출해보록하겠습니다.
@Column({ nullable: true })
passengerId: number;
@ManyToOne((type) => User, (user) => user.ridesAsPassenger)
passenger: User;
const ride = await Ride.findOne({ id: args.rideId });
console.log(ride);
Ride {
id: 7,
passengerId: 1, // passengerId의 FK만 준다.
}
@RelationId
github.com/typeorm/typeorm/blob/master/docs/decorator-reference.md#relationid
위와 같은 방법으로 사용해도 되지만 좀 더 확실하게 위 관계를 명시하는 방법은 @RelationId 데코레이터를 사용하는 것입니다.
되게 쉽죠~
@Entity()
export class Post {
@ManyToOne(type => Category)
category: Category;
@RelationId((post: Post) => post.category) // you need to specify target relation
categoryId: number;
}