1. Typeorm๊ณต๋ถ์ฉ ํ๋ก์ ํธ
Typeorm๊ณต๋ถ์ฉ ์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ญ์๋ค.
1nest new typeorm2yarn add @nestjs/typeorm typeorm pg
1# ์๋น์ค์ ์2services:3postgres:4image: postgres:155# ์คํ์๋ง๋ค ์ฌ์์6restart: always7# ๋์ปค์ปดํฌ์ฆ ํ์ผ์ ์กด์ฌํ๋ ์์น์ ์ค์ ๋ฐ์ดํฐ๋ฅผ hostOS์ ์ ์ฅ8volumes:9# ํ์ฌ ๋์ปค์ปดํฌ์ฆ ํ์ผ์ด ์กด์ฌํ๋ ๊ฒฝ๋ก : ์ด๋ฏธ์ง์์์กด์ฌํ๋ ๊ฒฝ๋ก ๋งคํ10- ./postgres-data:/var/lib/postgresql/data11ports:12# hostport:์ด๋ฏธ์ง์ํฌํธ13# 5432ํฌํธ ์์ฒญ -> ์ด๋ฏธ์ง์ ํฌํธ๋ก ์์ณฅ14- '5808:5432'15environment:16POSTGRES_USER: postgres17POSTGRES_PASSWORD: postgres18POSTGRES_DB: typeormstudy
1import { Module } from '@nestjs/common'2import { TypeOrmModule } from '@nestjs/typeorm'34import { AppController } from './app.controller'5import { AppService } from './app.service'67@Module({8imports: [9TypeOrmModule.forRoot({10// ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์11type: 'postgres',12host: '127.0.0.1',13port: 5808,14username: 'postgres',15password: 'postgres',16database: 'typeormstudy',17// entitiesํด๋์ ์์ฑํ PostsModel ๊ฐ์ ธ์ค๊ธฐ18entities: [],19synchronize: true,20}),21],22controllers: [AppController],23providers: [AppService],24})25export class AppModule {}
2. Column Annotation๋ค
src/entity/user.entity.ts ํ์ผ์ ๋ง๋ญ๋๋ค.
1import {2Column,3CreateDateColumn,4Entity,5Generated,6PrimaryGeneratedColumn,7UpdateDateColumn,8VersionColumn,9} from 'typeorm'1011@Entity()12export class UserModel {13/*** ID14* ์๋์ผ๋ก ID๋ฅผ ์์ฑํ๋ค.15*16* ๐ @PrimaryGeneratedColumn()17* Primary Column์ ๋ชจ๋ ํ ์ด๋ธ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์กด์ฌํด์ผ ํ๋ค18* ํ ์ด๋ธ ์์์ ๊ฐ๊ฐ์ Row๋ฅผ ๊ตฌ๋ถํ ์ ์๋ ์ปฌ๋ผ์ด๋ค.19* @PrimaryColumn()20*21* ๐ @PrimaryGeneratedColumn('uuid')22* PrimaryGeneratedColumn => ์์๋๋ก ์๋ก ์ฌ๋ผ๊ฐ๋ค.23* 1, 2, 3, 4, 5 -> 99999924*25* UUID : ์ ๋๋ก ๊ฒน์น์ง ์๋ ๊ณ ์ ํ ๊ฐ์ ๋ง๋ค์ด์ค26* ea36ed96-8d1c-44d9-9fbe-4ec6960e95a827*/28@PrimaryGeneratedColumn()29id: number3031@Column()32title: string3334/** ๋ฐ์ดํฐ ์์ฑ ์ผ์35* ๋ฐ์ดํฐ๊ฐ ์์ฑ๋๋ ๋ ์ง์ ์๊ฐ์ด ์๋์ผ๋ก ์ฐํ๋ค.36*/37@CreateDateColumn()38createdAt: Date3940/** ๋ฐ์ดํฐ ์์ ์ผ์41* ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ๋๋ ๋ ์ง์ ์๊ฐ์ด ์๋์ผ๋ก ์ฐํ๋ค.42*/43@UpdateDateColumn()44updateAt: Date4546/** ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ ๋ ๋๋ง๋ค 1์ฉ ์ฌ๋ผ๊ฐ๋ค47* ์ฒ์ ์์ฑ๋๋ฉด ๊ฐ์ 1์ด๋ค.48* save() ํจ์๊ฐ ๋ช ๋ฒ ๋ถ๋ ธ๋์ง ๊ธฐ์ตํ๋ค.49*/50@VersionColumn()51version: number5253/**54* ๐ @Generated('increment')55* additionalId: number56* PrimaryColumn์ ์๋๋ฐ, ๋ฐ์ดํฐ ์์ฑํ ๋๋ง๋ค, 1์ฉ ์ฌ๋ผ๊ฐ๋ ์ปฌ๋ผ57*58* ๐ Generated('uuid')59* additionalId: string60* ๋ ๋ง์ฐฌ๊ฐ์ง๋ก,61* PrimaryColumn์ ์๋๋ฐ, ๋ฐ์ดํฐ ์์ฑํ ๋๋ง๋ค, ๊ณ ์ ๊ฐ์ ๊ฐ์ง๋ ์ปฌ๋ผ62*/63@Column()64@Generated('uuid')65additionalId: string66}
1import { Module } from '@nestjs/common'2import { TypeOrmModule } from '@nestjs/typeorm'34import { AppController } from './app.controller'5import { AppService } from './app.service'6import { UserModel } from './entity/user.entity'78@Module({9imports: [10TypeOrmModule.forFeature([UserModel]),11TypeOrmModule.forRoot({12// ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์13type: 'postgres',14host: '127.0.0.1',15port: 5808,16username: 'postgres',17password: 'postgres',18database: 'typeormstudy',19// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ20entities: [UserModel],21synchronize: true,22}),23],24controllers: [AppController],25providers: [AppService],26})27export class AppModule {}
1import { Controller, Get, Param, Patch, Post } from '@nestjs/common'2import { InjectRepository } from '@nestjs/typeorm'3import { UserModel } from './entity/user.entity'4import { Repository } from 'typeorm'56@Controller()7export class AppController {8constructor(9@InjectRepository(UserModel)10private readonly userRepository: Repository<UserModel>11) {}1213@Post('users')14postUser() {15return this.userRepository.save({16title: 'test title',17})18}1920@Get('users')21getUsers() {22return this.userRepository.find()23}2425@Patch('users/:id')26async patchUser(@Param('id') id: string) {27const user = await this.userRepository.findOne({28where: { id: parseInt(id) },29})3031return this.userRepository.save({32...user,33title: user.title + '0',34})35}36}
3. Column Property ์ ๋ฆฌ
| ์์ฑ(property) | ์ค๋ช |
|---|---|
| type : ColumnType | ์นผ๋ผ ํ์ . varchar, text, int, bool๋ฑ ์นผ๋ผ ํ์ |
| name : string | ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ์นผ๋ผ ์ด๋ฆ, ๊ธฐ๋ณธ๊ฐ์ ํ๋กํผํฐ ์ด๋ฆ์ ๋ฐ๋ฆ |
| nullable : boolean | null ๊ฐ์ด ๊ฐ๋ฅํ์ง ์ฌ๋ถ. ๊ธฐ๋ณธ๊ฐ์ false |
| update : boolean | ์ ๋ฐ์ดํธ ๊ฐ๋ฅ ์ฌ๋ถ. false์ผ ๊ฒฝ์ฐ ์ ์ฅ ํ ์ ๋ฐ์ดํธ ๋ถ๊ฐ. ๊ธฐ๋ณธ๊ฐ true |
| select : boolean | ์ฟผ๋ฆฌ ์คํ ์ ํ๋กํผํฐ๋ฅผ ๊ฐ์ ธ์ฌ์ง ๊ฒฐ์ . false์ผ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค์ง ์๋๊ฒ ๊ธฐ๋ณธ |
| default : string | ์นผ๋ผ ๊ธฐ๋ณธ๊ฐ |
| unique : boolean | unique constraint ์ ์ฉ ์ฌ๋ถ. ๊ธฐ๋ณธ false |
| comment : string | ์นผ๋ผ ์ฝ๋ฉํธ. ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ง์๋์ง ์์ |
| enum : string[] | ์นผ๋ผ์ ์ ๋ ฅ ๊ฐ๋ฅํ ๊ฐ์ enum์ผ๋ก ๋์ด |
| Array : boolean | ์นผ๋ผ array ํ์
์ผ๋ก ์์ฑ (e.g. int[]) |
1// user.entity.ts ์๋ต2@Column({3type: 'varchar',4name: 'title',5length: 300,6nullable: true,7update: true,8select: false,9default: 'default value',10unique: false,11})12title: string
1// app.controller.ts ์๋ต2@Post('users')3postUser() {4return this.userRepository.save({5// title: 'test title',6})7}89@Get('users')10getUsers() {11return this.userRepository.find({12select: { id: true, title: true },13})14}
4. Enum Column
1export enum Role {2USER = 'user',3ADMIN = 'admin',4}56@Entity()7export class UserModel {8// ์๋ต9title: string1011@Column({12type: 'enum',13enum: Role,14default: Role.USER,15})16role: Role1718// ์๋ต19}
1@Post('users')2postUser() {3return this.userRepository.save({4// title: 'test title',5role: Role.ADMIN, // ๊ด๋ฆฌ์ ์ญํ ์ ๋ฃ๊ณ ์ถ์ ๋6})7}89// app.controller.ts์ select์ต์ ์ง์ฐ๊ธฐ10@Get('users')11getUsers() {12return this.userRepository.find({})13}
5. Entity Embedding
src/entity/person.entity.ts ํ์ผ์ ๋ง๋ ๋ค.
1import { Column, Entity, PrimaryColumn } from 'typeorm'23export class Name {4@Column()5first: string67@Column()8last: string9}1011@Entity()12export class StudentModel {13@PrimaryColumn()14id: number1516@Column(() => Name)17name: Name1819@Column()20class: string21}2223@Entity()24export class TeacherModel {25@PrimaryColumn()26id: number2728@Column(() => Name)29name: Name3031@Column()32salary: number33}
app.module.ts์ ์์ฑํ ๋ชจ๋์ ์ถ๊ฐํ๋ค.
1// ์๋ต2@Module({3imports: [4TypeOrmModule.forFeature([UserModel]),5TypeOrmModule.forRoot({6// ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์7type: 'postgres',8host: '127.0.0.1',9port: 5808,10username: 'postgres',11password: 'postgres',12database: 'typeormstudy',13// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ14entities: [UserModel, StudentModel, TeacherModel],15synchronize: true,16}),17],18controllers: [AppController],19providers: [AppService],20})21export class AppModule {}
DB์ ๋ค์ด๊ฐ ์ปฌ๋ผ๋ช
์ ํ์ธํด๋ณด๋ฉด, nameFirst, nameLast๋ก ๋ค์ด๊ฐ ๊ฒ์ ํ์ธํ ์ ์๋ค.
6. Entity Inheritance
src/entity/inheritance.entity.ts ํ์ผ์ ๋ง๋ ๋ค.
1import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'23export class BaseModel {4@PrimaryGeneratedColumn()5id: number67@CreateDateColumn()8createdAt: Date910@UpdateDateColumn()11updateat: Date12}1314@Entity()15export class BookModel extends BaseModel {16@Column()17name: string18}1920@Entity()21export class CarModel extends BaseModel {22@Column()23brand: string24}
app.module.ts์ ์์ฑํ ๋ชจ๋์ ์ถ๊ฐํ๋ค.
1@Module({2imports: [3TypeOrmModule.forFeature([UserModel]),4TypeOrmModule.forRoot({5// ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์6type: 'postgres',7host: '127.0.0.1',8port: 5808,9username: 'postgres',10password: 'postgres',11database: 'typeormstudy',12// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ13entities: [14UserModel,15StudentModel, //16TeacherModel,17BookModel,18CarModel,19],20synchronize: true,21}),22],23controllers: [AppController],24providers: [AppService],25})26export class AppModule {}
DB์ ๋ค์ด๊ฐ ์ปฌ๋ผ๋ช ์ ํ์ธํด๋ณด๋ฉด, ์์๋ฐ์ ์์ฑ์ด ๋ค์ด๊ฐ ๊ฒ์ ํ์ธํ ์ ์๋ค. ํ์์๋ ์์ ๊ฐ์ ์ผ๋ฐ์ ์ธ ์์์ ์ฐ๋ ๊ฒ์ด ์ข๋ค. ๋ค๋ง, ๊ตณ์ด ํ๋์ ํ ์ด๋ธ๋ก ๊ด๋ฆฌํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
1import {2ChildEntity,3Column,4CreateDateColumn,5Entity,6PrimaryGeneratedColumn,7TableInheritance,8UpdateDateColumn,9} from 'typeorm'1011export class BaseModel {12@PrimaryGeneratedColumn()13id: number1415@CreateDateColumn()16createdAt: Date1718@UpdateDateColumn()19updateat: Date20}2122@Entity()23export class BookModel extends BaseModel {24@Column()25name: string26}2728@Entity()29export class CarModel extends BaseModel {30@Column()31brand: string32}3334@Entity()35@TableInheritance({36column: {37name: 'type',38type: 'varchar',39},40})41export class SingleBaseModel {42@PrimaryGeneratedColumn()43id: number4445@CreateDateColumn()46createdAt: Date4748@UpdateDateColumn()49updateat: Date50}5152@ChildEntity()53export class ComputerModel extends SingleBaseModel {54@Column()55brand: string56}5758@ChildEntity()59export class AirplaneModel extends SingleBaseModel {60@Column()61country: string62}
๋ง์ฐฌ๊ฐ์ง๋ก app.module.ts์ ์์ฑํ ๋ชจ๋์ ์ถ๊ฐํ๋ค.
1@Module({2imports: [3TypeOrmModule.forFeature([UserModel]),4TypeOrmModule.forRoot({5// ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์6type: 'postgres',7host: '127.0.0.1',8port: 5808,9username: 'postgres',10password: 'postgres',11database: 'typeormstudy',12// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ13entities: [14UserModel,15StudentModel, //16TeacherModel,17BookModel,18CarModel,19SingleBaseModel,20ComputerModel,21AirplaneModel,22],23synchronize: true,24}),25],26controllers: [AppController],27providers: [AppService],28})29export class AppModule {}
DB์ ๋ค์ด๊ฐ ์ปฌ๋ผ๋ช ์ ํ์ธํด๋ณด๋ฉด, ์์ ์ปฌ๋ผ์ด ๋ค์ด๊ฐ single_base_model ํ๋๋ง ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
7. Relationship ์ด๋ก
TypeORM์ด ์ ๊ณตํ๋ 4๊ฐ์ง Relationship
| ๊ด๊ณ | ์ค๋ช |
|---|---|
@OneToOne | A ํ ์ด๋ธ์ Row ํ๋์ B ํ ์ด๋ธ์ Row ํ๋๊ฐ ์ฐ๊ฒฐ๋๋ ๊ด๊ณ |
@ManyToOne | A ํ ์ด๋ธ์ Row ์ฌ๋ฌ ๊ฐ์ B ํ ์ด๋ธ์ Row ํ๋๊ฐ ์ฐ๊ฒฐ๋๋ ๊ด๊ณ |
@OneToMany | A ํ ์ด๋ธ์ Row ํ๋์ B ํ ์ด๋ธ์ Row ์ฌ๋ฌ๊ฐ๊ฐ ์ฐ๊ฒฐ๋๋ ๊ด๊ณ |
@ManyToMany | A ํ ์ด๋ธ์ Row ์ฌ๋ฌ ๊ฐ์ B ํ ์ด๋ธ์ Row ์ฌ๋ฌ ๊ฐ๊ฐ ์ฐ๊ฒฐ๋๋ ๊ด๊ณ |
7.1 Relationship Annotation ์ ์ฉ
- ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ํ์ ์ ๋ฐํํ๋ ํจ์๋ฅผ ์ ๋ ฅํ๋ค. (Class Transformer Type๊ณผ ๊ฐ์ ๊ฐ๋ )
- ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ ์ ๋ ฅํ ํด๋์ค์ ์นผ๋ผ์ค ํ๋๋ฅผ ์ ๋ ฅํ๋ค. ์ด ์นผ๋ผ์ ์๋ก ๊ด๋ จ์ง์ ํ๋กํผํฐ์ฌ์ผํ๋ค
- e.g. ManyToOne ๊ด๊ณ์ด๋ photo ํ ์ด๋ธ์ user_id ์นผ๋ผ์ด ์์ฑ๋๋ฉฐ user ํ ์ด๋ธ๊ณผ ๊ด๊ณ๊ฐ ํ์ฑ๋๋ค
- ํน์ photo์ ๊ด๋ จ์๋ user๋ photo.user๋ก ๋ถ๋ฌ์ฌ ์ ์๊ณ user์ ๊ด๋ จ์๋ photo๋ค์ user.photos๋ก ๋ถ๋ฌ ์ฌ ์ ์๋ค
1@Entity()2export class Photo {3@PrimaryGeneratedColumn()4id: number56@Column()7url: string89// ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ : ํ์ ์ ๋ฐํํ๋ ํจ์10// ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ : ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ ์ ๋ ฅํ ํด๋์ค์ ์นผ๋ผ์ค ํ๋๋ฅผ ์ ๋ ฅ11@ManyToOne(() => User, (user) => user.photos)12user: User13}
1@Entity()2export class User {3@PrimaryGeneratedColumn()4id: number56@Column()7name: string89@OneToMany(() => Photo, (photo) => photo.user)10photos: Photo[]11}
7.2 ManyToOne & OneToMany ๊ด๊ณ
- photo ํ
์ด๋ธ์๋ user_id ์นผ๋ผ์ด ์๋์ผ๋ก ์๊ธด๋ค. ๋ค์ด๋ฐ ํจํด์
{์๋ ํ ์ด๋ธ ์ด๋ฆ}_id - user_id๋ user ํ ์ด๋ธ์ id ์นผ๋ผ์ Foreign Key๋ก ๋ ํผ๋ฐ์คํ๋ค
- user ํ
์ด๋ธ์ ์ถ๊ฐ๋ก ์นผ๋ผ์ด ์์ฑ๋์ง ์๋๋ค.
- ์๋ ManyToOne ๋๋ OneToMany ๊ด๊ณ๋ Foreign Key ๋ ํผ๋ฐ์ค๋ฅผ ๋ค๊ณ ์๋ ํ ์ด๋ธ์ด Many ์ ์ฅ์ด๋ค
| Photo ํ ์ด๋ธ | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| url | varchar | |
| user_id | varchar | FOREIGN KEY |
| User ํ ์ด๋ธ | ||
|---|---|---|
| id | int | PRIMARY KEY AUTO_INCREMENT |
| name | varchar |
7.3 OneToOne ๊ด๊ณ
- OneToOne Relationship๋ ๋ง์ฐฌ๊ฐ์ง๋ก Annotation์ ์ํ๋ ํ๋กํผํฐ์ ์ ์ํด์ฃผ๋ฉด ๋๋ค
- ManyToOne์ ์๋์ ๋ ํผ๋ฐ์ค๋ฅผ ๊ฐ๋ ํ ์ด๋ธ์ด ๋ช ํํ๋ค
- OneToOne์ ๋ ํ ์ด๋ธ ๋๊ฐ ๋ ํผ๋ฐ์ค๋ฅผ ๋ค๊ณ ์์ด๋ ์๊ด์ด ์๊ธฐ ๋๋ฌธ์ ์ด๋ค ํ ์ด๋ธ์ด ๋ ํผ๋ฐ์ค๋ฅผ ๋ค๊ณ ์์์ง ๋ช ์ํด์ผ ํ๋ค
@JoinTable Annotation์ ์ฌ์ฉํด์ ์ด๋ค ํ๋กํผํฐ๊ฐ ๋ ํผ๋ฐ์ค ๋ค๊ณ ์์์ง ์ ํด ์ค ์ ์๋ค@JoinTable์ ๊ผญ ํ์ชฝ์๋ง ์ ์ฉํด์ผํ๋ค. ๋ ๋ชจ๋ ์ ์ฉํ๋๊ฑด ๊ฐ๋ฅํ๊ณ ์๋ฏธ๋ ์๋ค
1@Entity()2export class Profile {3@PrimaryGeneratedColumn()4id: number56@Column()7gender: string89@Column()10photos: string1112@OneToOne(() => User, (user) => user.profile)13user: User14}
1@Entity()2export class User {3@PrimaryGeneratedColumn()4id: number56@Column()7name: string89@OneToOne(() => Profile)10@JoinColumn()11profile: Profile12}
7.4 ManyToMany ๊ด๊ณ
- ManyToMany Relationship๋ OneToOne Relationship๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก @JoinTable Annotation์ ํ์ชฝ์ ์ ์ฉ ํด์ค์ผํ๋ค
- ์ค๊ฐ ํ ์ด๋ธ์ด ์์ฑ๋ ๋ @JoinTable์ด ์ ์ฉ๋ ํ ์ด๋ธ ์ด๋ฆ์ด ๋จผ์ ์์นํ๊ฒ๋๋ค.
- ManyToMany Annotation์ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก ์ค๊ฐ ํ ์ด๋ธ์ด ์์ฑ๋๋ค
- ์ค๊ฐ ํ
์ด๋ธ์ ์นผ๋ผ์ ๊ฐ๊ฐ ๋ ํผ๋ฐ์ค ํ
์ด๋ธ์
{ํ ์ด๋ธ ์ด๋ฆ}_id๋ก ์ ์๋๋ค
1@Entity()2export class Category {3@PrimaryGeneratedColumn()4id: number56@Column()7name: string89@ManyToMany(() => Question)10questions: Question[]11}
1@Entity()2export class Question {3@PrimaryGeneratedColumn()4id: number56@Column()7title: string89@Column()10text: string1112@ManyToMany(() => Category)13@JoinTable()14categories: Category[]15}
8. 1:1 ๊ด๊ณ ์์
src/entity/profile.entity.ts ํ์ผ์ ๋ง๋ ๋ค.
1import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm'2import { UserModel } from './user.entity'34@Entity()5export class ProfileModel {6@PrimaryGeneratedColumn()7id: number89// UserModel์ user์ profile ์ปฌ๋ผ๊ณผ 1:1 ์ฐ๊ฒฐ10@OneToOne(() => UserModel, (user) => user.profile)11// ์๋๋ฐฉ ํ ์ด๋ธ์ id๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ(๋ง์ฝ ์๋๋ฐฉ์ด ๊ฐ๊ณ ์์ผ๋ฉด ์๋๋ฐฉ์ด ์ด ํ ์ด๋ธ id๋ฅผ ๊ฐ์ง)12@JoinColumn()13user: UserModel1415@Column()16profileImg: string17}
์ฐ๊ฒฐํ ๋ชจ๋ธ(user.entity.ts)์ 1:1๋ก ์ฐ๊ฒฐํ ๋ชจ๋ธ์ ์ถ๊ฐํ๋ค.
1// ์๋ต2// ProfileModel์ profile์ user ์ปฌ๋ผ๊ณผ 1:1 ์ฐ๊ฒฐ3@OneToOne(() => ProfileModel, profile => profile.user)4profile: ProfileModel
app.module.ts์ ์์ฑํ ๋ชจ๋(ProfileModel)์ ์ถ๊ฐํ๋ค.
1@Module({2imports: [3// ProfileModel ์ถ๊ฐ4TypeOrmModule.forFeature([UserModel, ProfileModel]),5TypeOrmModule.forRoot({6// ์๋ต7// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ8entities: [9// ์๋ต10ProfileModel,11],12synchronize: true,13}),14],15controllers: [AppController],16providers: [AppService],17})
user ์ํฐํฐ์ title์ ์ง์ฐ๊ณ ๋์ , email ์ปฌ๋ผ์ ์ถ๊ฐํ๋ค.
๊ธฐ์กด ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋์ด์๋ ๊ณณ(postgres-data)์ ์ง์ ๋ค๊ฐ ๋ค์ ์์ฑํ๋ค.(docker-compose up)
1// user.entity.ts2// title: string์ ์ฃผ์์ฒ๋ฆฌ3@Column()4email: string
app ์ปจํธ๋กค๋ฌ๋ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ๋ค.
1// ์๋ต2@Controller()3export class AppController {4constructor(5@InjectRepository(UserModel)6private readonly userRepository: Repository<UserModel>,7@InjectRepository(ProfileModel)8private readonly profileRepository: Repository<ProfileModel>9) {}1011@Post('users')12postUser() {13return this.userRepository.save({})14}1516@Get('users')17getUsers() {18return this.userRepository.find({19// ์ฐ๋๋ ๋ฐ์ดํฐ ์ปฌ๋ผ(profile)๋ ๊ฐ์ ธ์ค๊ธฐ20relations: {21profile: true,22},23})24}2526@Patch('users/:id')27async patchUser(@Param('id') id: string) {28const user = await this.userRepository.findOne({29where: { id: parseInt(id) },30})3132return this.userRepository.save({33...user,34})35}3637@Post('user/profile')38async createUserAndProfile() {39const user = await this.userRepository.save({40email: 'asd@gmail.ai',41})42const profile = await this.profileRepository.save({43profileImg: 'asdf.png',44user,45})46return user47}48}
9. M:1 & 1:M ๊ด๊ณ ๊ตฌํ
src/entity/post.entity.ts ํ์ผ์ ๋ง๋ ๋ค.
1import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'2import { UserModel } from './user.entity'34@Entity()5export class PostModel {6@PrimaryGeneratedColumn()7id: number89// 1:M ๊ด๊ณ์ด๋ posts๋ก ๋ณต์ํ์ผ๋ก ์ ์ธ10@ManyToOne(() => UserModel, (user) => user.posts)11author: UserModel1213@Column()14title: string15}
ํ ์ด๋ธ์ด ์ด๋ ์ง์ ์ ๋ฐ๋ผ๋ณด๋์ ๋ฐ๋ผ M:1์ด๋ 1:M์ด ๋๋ค.
1// user.entity.ts ์๋ต2@OneToMany(() => PostModel, post => post.author)3posts: PostModel[]
app.module.ts์ ์์ฑํ ๋ชจ๋(PostModel)์ ์ถ๊ฐํ๋ค.
1@Module({2imports: [3TypeOrmModule.forFeature([4UserModel,5ProfileModel,6PostModel, // ์ถ๊ฐ7]),8TypeOrmModule.forRoot({9// ์๋ต10// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ11entities: [12// ์๋ต13PostModel,14],15synchronize: true,16}),17],18controllers: [AppController],19providers: [AppService],20})
ํ์ธ์ฉ api๋ฅผ app.controller.ts์ ๋ง๋ค๊ณ ํฌ์คํธ๋งจ์์ ํ์ธํด๋ณด์ธ์.
1// ์๋ต23@Controller()4export class AppController {5constructor(6@InjectRepository(UserModel)7private readonly userRepository: Repository<UserModel>,8@InjectRepository(ProfileModel)9private readonly profileRepository: Repository<ProfileModel>,10@InjectRepository(PostModel)11private readonly postRepository: Repository<PostModel>12) {}1314// ์๋ต15@Get('users')16getUsers() {17return this.userRepository.find({18relations: {19profile: true,20posts: true, // ์ถ๊ฐ21},22})23}2425@Post('user/post')26async createUserAndPost() {27const user = await this.userRepository.save({28email: 'postuser@gmail.ai',29})30await this.postRepository.save({31author: user,32title: 'post 1',33})34await this.postRepository.save({35author: user,36title: 'post 2',37})3839return user40}41}
10. M : M ๊ด๊ณ ๊ตฌํ
src/entity/tag.entity.ts ํ์ผ์ ๋ง๋ ๋ค.
1import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm'2import { PostModel } from './post.entity'34@Entity()5export class TagModel {6@PrimaryGeneratedColumn()7id: number89// M:M ์ฐ๊ฒฐ์ด๊ธฐ ๋๋ฌธ์ ๋ ๋ค ๋ณต์๋ก ์ ์ธ10@ManyToMany(() => PostModel, (post) => post.tags)11posts: PostModel[]1213@Column()14name: string15}
post.entity.ts์ tags๋ฅผ ๋ค๋๋ค ๊ด๊ณ๋ก ์ฐ๊ฒฐํฉ๋๋ค.
1import { Column, Entity, JoinTable, ManyToMany, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'2import { UserModel } from './user.entity'3import { TagModel } from './tag.entity'45@Entity()6export class PostModel {7@PrimaryGeneratedColumn()8id: number910// 1:M ๊ด๊ณ์ด๋ posts๋ก ๋ณต์ํ์ผ๋ก ์ ์ธ11@ManyToOne(() => UserModel, (user) => user.posts)12author: UserModel1314// M:M ์ฐ๊ฒฐ์ด๊ธฐ ๋๋ฌธ์ ๋ ๋ค ๋ณต์๋ก ์ ์ธ15@ManyToMany(() => TagModel, (tag) => tag.posts)16// M:M ์ฐ๊ฒฐ์์ JoinTable์ ๋ ์ค ํ๋ ์๋ฌด๊ตฐ๋ฐ ์ ์ธํด์ฃผ๋ฉด ๋๋ค17@JoinTable()18tags: TagModel[]1920@Column()21title: string22}
app.module.ts์ ์์ฑํ ๋ชจ๋(TagModel)์ ์ถ๊ฐํ๋ค.
1// ์๋ต2@Module({3imports: [4TypeOrmModule.forFeature([5UserModel,6ProfileModel, //7PostModel,8TagModel,9]),10TypeOrmModule.forRoot({11// ์๋ต12// entitiesํด๋์ ์์ฑํ Model ๊ฐ์ ธ์ค๊ธฐ13entities: [14// ์๋ต15TagModel,16],17synchronize: true,18}),19],20controllers: [AppController],21providers: [AppService],22})23export class AppModule {}
app ์ปจํธ๋กค๋ฌ์ tag ์์ฒญ์ ์ถ๊ฐํ๋ค.
1// ์๋ต2@Controller()3export class AppController {4constructor(5@InjectRepository(UserModel)6private readonly userRepository: Repository<UserModel>,7@InjectRepository(ProfileModel)8private readonly profileRepository: Repository<ProfileModel>,9@InjectRepository(PostModel)10private readonly postRepository: Repository<PostModel>,11@InjectRepository(TagModel)12private readonly tagRepository: Repository<TagModel>13) {}1415// ์๋ต1617@Post('posts/tags')18async createPostsTags() {19const post1 = await this.postRepository.save({20title: 'NestJS ์์ ',21})2223const post2 = await this.postRepository.save({24title: 'ํ๋ก๊ทธ๋๋ฐ ์์ ',25})2627const tag1 = await this.tagRepository.save({28name: 'Javascript',29posts: [post1, post2],30})3132const tag2 = await this.tagRepository.save({33name: 'Typescript',34posts: [post1],35})3637const post3 = await this.postRepository.save({38title: 'NextJS ์์ ',39tags: [tag1, tag2],40})4142return true43}4445@Get('posts')46getPosts() {47return this.postRepository.find({48relations: {49tags: true,50},51})52}5354@Get('tags')55getTags() {56return this.tagRepository.find({57relations: {58posts: true,59},60})61}62}
ํฌ์คํธ๋งจ์ผ๋ก ์์ฒญํด ํ์ธํด๋ณด์ธ์.
11. Relation Options
postgres-data ํ์ผ์ ์ง์ ๋ฐ์ดํฐ๋ฅผ ์ด๊ธฐํํ๊ณ ๋ค์ ์์ํฉ๋๋ค.
user.entity.ts์์ ๊ด๊ณ๋ก ๋ฃ์ ์ ์๋ ์ต์
์ ๋ํด ์์๋ด
์๋ค.
1// ProfileModel์ profile์ user ์ปฌ๋ผ๊ณผ 1:1 ์ฐ๊ฒฐ2@OneToOne(() => ProfileModel, profile => profile.user, {3// find() ์คํํ ๋๋ง๋ค ํญ์ ๊ฐ์ด ๊ฐ์ ธ์ฌ relation ์ค์ (๊ธฐ๋ณธ๊ฐ false)4eager: true,5// ์ ์ฅํ ๋, relation์ ํ ๋ฒ์ ๊ฐ์ด ์ ์ฅ(๊ธฐ๋ณธ๊ฐ false)6cascade: true,7// null์ด ๊ฐ๋ฅํ์ง ์ฌ๋ถ(๊ธฐ๋ณธ๊ฐ true)8nullable: true,9// ๊ด๊ณ๋ฅผ ์ญ์ ํ์ ๋, ์ด๋ป๊ฒ ์ญ์ ํ ๊ฒ์ธ์ง10// - NO ACTION : ์๋ฌด๊ฒ๋ ์ํจ11// - CASCADE : ์ฐธ์กฐํ๋ row๋ ๊ฐ์ด ์ญ์ 12// - SET NULL : ์ฐธ์กฐํ๋ row์์ ์ฐธ์กฐ id๋ฅผ null๋ก ๋ณ๊ฒฝ13// - set default : ๊ธฐ๋ณธ ์ธํ ์ผ๋ก ์ค์ (ํ ์ด๋ธ์ ๊ธฐ๋ณธ ์ธํ )14// - RESTRICT : ์ฐธ์กฐํ๊ณ ์๋ row๊ฐ ์๋ ๊ฒฝ์ฐ ์ฐธ์กฐ๋นํ๋ row ์ญ์ ๋ถ๊ฐ15onDelete: 'NO ACTION',16})17profile: ProfileModel
๊ด๊ณ ์ญ์ ์ต์
ํ
์คํธ์ฉ์ app.controller.ts์ ๋ฃ์ด์ ํ์ธํด๋ณธ๋ค
1@Delete('user/profile/:id')2async deleteProfile(@Param('id') id: string) {3await this.profileRepository.delete(+id)4}
12. FindManyOptions ํ๋ผ๋ฏธํฐ
app.controller.ts์์ find()์ ๊ฐ์ ธ์ฌ ์ ์๋ ์ต์
์ ๋ํด ์์๋ด
์๋ค.
1@Get('users')2getUsers() {3return this.userRepository.find({4// ์ด๋ค ์์ฑ์ ์ ํํ ์ง (๊ธฐ๋ณธ์ ๋ชจ๋ ์์ฑ์ ๊ฐ์ ธ์ด)5// select๋ฅผ ์ ์ํ๋ฉด, ์ ์ํ ์์ฑ๋ง ๊ฐ์ ธ์จ๋ค6select: {7id: true,8email: true,9version: true,10profile: {11id: true,12},13},14// ํํฐ๋งํ ์กฐ๊ฑด์ ์ ๋ ฅํ๋ค ({}์์์๋ ์ ๋ถ and ์กฐ๊ฑด์ผ๋ก ํํฐ๋ง)15where: [16// id๊ฐ 3์ด๊ฑฐ๋ or version์ด 117{18id: 3,19},20{21version: 1,22},23],24// ------ ๋ค๋ฅธ ๊ด๊ณ๋ฅผ ํํฐ๋งํ๋ ๋ฒ25// where: {26// profile: {27// id: 3,28// },29// },30// ๊ด๊ณ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฒ31relations: {32profile: true,33},34// ์ค๋ฆ์ฐจ(ASC)-๊ธฐ๋ณธ, ๋ด๋ฆผ์ฐจ(DESC)35order: {36id: 'DESC',37},38// ์ฒ์ ๋ช ๊ฐ๋ฅผ ์ ์ธํ ์ง (๊ธฐ๋ณธ์ 0) 1์ด๋ฉด 1๊ฐ ์คํต39skip: 0,40// ๋ช ๊ฐ๋ฅผ ๊ฐ์ ธ์ฌ์ง (๊ธฐ๋ณธ๊ฐ์ 0, ์ ์ฒด) 1์ด๋ฉด 1๊ฐ๋ง ๊ฐ์ ธ์ด41take: 0,42})43}
13. Typeorm ์ ํธ๋ฆฌํฐ ๋๊ตฌ
app.controller.ts์์ find()์ ๊ฐ์ ธ์ฌ ์ ์๋ ์ ํธ๋ฆฌํฐ ์ต์
์ ๋ํด ์์๋ณด์.
1@Post('users')2async postUser() {3for (let i = 0; i < 100; i++) {4await this.userRepository.save({5email: `user-${i}@google.com`,6})7}8}910@Get('users')11getUsers() {12return this.userRepository.find({13where: {14// ๐ (1) 1์ด ์๋ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค๊ธฐ15id: Not(1),1617// ๐ (2) 30 ๋ฏธ๋ง์ ์ ์ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค๊ธฐ18id: LessThan(30),1920// ๐ (3) 30 ์ดํ์ ์ ์ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค๊ธฐ21id: LessThanOrEqual(30),2223// ๐ (4) 30 ์ด๊ณผ์ ๋ง์ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค๊ธฐ24id: MoreThan(30)2526// ๐ (5) 30 ์ด์์ ๋ง์ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค๊ธฐ27id: MoreThanOrEqual(30)2829// ๐ (6) ๊ฐ์ ๊ฒฝ์ฐ ๊ฐ์ ธ์ค๊ธฐ30id: Equal(30)3132// ๐ (7) ์ ์ฌ๊ฐ, %๋ก ์ ์ฌํ ๋ฌธ์ ์ฐพ๊ธฐ (๋์๋ฌธ์ ๊ตฌ๋ถํจ)33email: Like('%0@google%'),3435// ๐ (8) ์ ์ฌ๊ฐ, %๋ก ์ ์ฌํ ๋ฌธ์ ์ฐพ๊ธฐ (๋์๋ฌธ์ ๊ตฌ๋ถ์ํจ)36email: ILike('%GOOGLE%'),3738// ๐ (9) ์ฌ์ด๊ฐ, 10~15๋ฒ ์ฌ์ด๊น์ง์ ๊ฐ39id: Between(10, 15),4041// ๐ (10) ํด๋น๋๋ ์ฌ๋ฌ ๊ฐ์ ๊ฐ, 1, 3, 5, 7, 99์ id ์ฐพ๊ธฐ42id: In([1, 3, 5, 7, 99]),4344// ๐ (11) ID๊ฐ null์ธ ๊ฒฝ์ฐ ์ฐพ๊ธฐ45id: IsNull(),46},47// ์ฃผ์ ์๋ต48})49}
๋ฐ์ดํฐ๋ฅผ ์ด๊ธฐํํ๊ณ , 1~100๊ฐ์ ์์์ ์ ์ ๋ฅผ ๋ง๋ ํ, ๊ฐ๊ฐ์ ์ ํธ๋ฆฌํฐ๋ฅผ ํ์ธํ๋ค.
14. Repository
Repository๋ ์ง์ ํ Entity์ ๋ํ CRUD ์ฟผ๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ํด์ค๋ค- TypeORM์ ์ ์๋ผ์๋ ๋ฉ์๋๋ค์ ์ฌ์ฉํด์ ์ง์ SQL์ ์์ฑํ์ง ์๋๋ผ๋ ๋ฐ์ดํฐ ๊ด๋ฆฌ๋ฅผ ํ ์ ์๋ค
1const userRepository = dataSources.getRepository(User)2const user = await userRepository.findOne({ id: 1 })3user.name = '๋ญ์๊ธฐ'4await userRepository.save(user)
Repository์ ์ฃผ์ ๊ธฐ๋ฅ๋ค
| ์ฃผ์ ๊ธฐ๋ฅ๋ค | ์ฃผ์ ๋ฉ์๋ |
|---|---|
| Create & Delete ๊ด๋ จ | create(), save(), upsert(), delete(), softDelete(), restore() |
| Update ๊ด๋ จ | update(), increment(), decrement() |
| Find ๊ด๋ จ | find(), findAndCount(), findOne(), exists(), preload(), query() |
| ํต๊ณ ๊ด๋ จ ๋ฐ ๊ธฐํ | count(), sum(), average(), minimum(), maximum(), query() |
14.1 create()
- create() ๋ฉ์๋๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์ญํ ์ ํ๋ค
- create()๋ save()์ ๋ค๋ฅด๊ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ์ง ์๊ณ โ๊ฐ์ฒดโ๋ฅผ ์์ฑ๋ง ํ๋ค๋๊ฑธ ๊ผญ ๊ธฐ์ตํ์!
1// const user = new User();2const user = repository.create()3const user = repository.create({4id: 1,5firstName: 'Timber',6lastName: 'Saw',7})
14.2 save()
- save() ๋ฉ์๋์ ์ ์ฅํ Entity๋ฅผ ์ ๋ ฅ ํด์ฃผ๋ฉด ์ ์ฅ ํ ์ ์๋ค
- create()์ ๋ค๋ฅด๊ฒ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ์ด ๋๋ค.
- ๋ง์ฝ์ ์ด๋ฏธ Row๊ฐ ์กด์ฌํ๋ค๋ฉด (primary key ๊ฐ์ผ๋ก ๊ตฌ๋ถ) ์ ๋ฐ์ดํธํ๋ค. (์ฃผ์ํ ๊ฒ!)
- ์ฌ๋ฌ ๊ฐ์ฒด๋ฅผ ํ๋ฒ์ ์ ์ฅ๋ ๊ฐ๋ฅํ๋ค
1await repository.save(user)2await repository.save([category1, category2, category3])
14.3 upsert()
- update์ insert๋ฅผ ํฉ์น๊ฒ upsert()๋ค
- ๋ฐ์ดํฐ ์์ฑ ์๋๋ฅผ ํ ํ ๋ง์ฝ์ ์ด๋ฏธ ์กด์ฌํ๋ ๋ฐ์ดํฐ๋ผ๋ฉด ์ ๋ฐ์ดํธ๋ฅผ ์งํํ๋ค
- save()์ ๋ค๋ฅด๊ฒ upsert()๋ ํ๋์ transaction์์ ์์ ์ด ์คํ๋๋ค
1await repository.upsert(2[3{ externalId: 'abc123', firstName: 'Timber' },4{ externalId: 'bca321', firstName: 'Saw' },5],6['externalId']7)8/** executes9* INSERT INTO user10* VALUES11* (externalId: "abc123", firstName: 'Timber'),12* (externalId: "bca321", firstName: 'Saw')13* ON CONFLICT (externalId) DO UPDATE firstName = EXCLUDED.firstName14*/
14.4 delete()
- Row๋ฅผ ์ญ์ ํ ๋ ์ฌ์ฉ๋๋ค
- ๋์ฒด์ ์ผ๋ก Primary Key๋ฅผ ์ฌ์ฉํด์ ์ญ์ ํ๋ค
- ์ํ๋ค๋ฉด findOptionsWhere ์กฐ๊ฑด์ผ๋ก ์ฌ๋ฌ ๊ฐ์ ์ญ์ ํ ์๋ ์๋ค.
1await repository.delete(1)2await repository.delete([1, 2, 3])3await repository.delete({ firstName: 'Timber' })
14.5 softDelete(), restore()
- softDelete()๋ ๋น์๊ตฌ์ ์ผ๋ก ์ญ์ ํ๋ ๊ธฐ๋ฅ์ด๋ค
- restore()๋ฅผ ์คํํ๋ฉด softDelete() ํ๋ Row๋ฅผ ๋ณต๊ตฌ ํ ์ ์๋ค
1await repository.softDelete(1) // ์ญ์ 2await repository.restore(1) // ๋ณต๊ตฌ
14.6 update()
- ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ ๊ฒ์ ์กฐ๊ฑด์ ์ ๋ ฅํด์ค๋ค
- ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ ๋ณ๊ฒฝ ํ๋๋ฅผ ์ ๋ ฅํด์ค๋ค
1// UPDATE user2// SET category = ADULT3// WHERE age = 184await repository.update({ age: 18 }, { category: 'ADULT' })56// UPDATE user7// SET firstName = ๋ญ์๊ธฐ8// WHERE id = 19await repository.update(1, { firstName: '๋ญ์๊ธฐ' })
14.7 find(), findOne(), findAndCount()
find(): ํด๋น๋๋ Row๋ฅผ ๋ชจ๋ ๋ฐํํ๋คfindOne(): ํด๋น๋๋ ์ฒซ๋ฒ์งธ Row๋ฅผ ๋ฐํํ๋ค. ์์๊ฒฝ์ฐ nullfindAndCount(): ํด๋น๋๋ Row์ ์ ์ฒด ๊ฐฏ์๋ฅผ ๋ฐํํ๋ค.
1const rows = await repository.find({2where: { firstName: 'Timber' },3})4const rows = await repository.findOne({5where: { firstName: 'Timber' },6})7const rows = await repository.findAndCount({8where: { firstName: 'Timber' },9})
14.7.1 FindOptions
1export interface FindOneOptions<Entity = any> {2select?: FindOneOptionsSelect<Entity> // ๋ถ๋ฌ์ฌ Column์ ์ง์ 3where?: FindOptionsWhere<Entity>[] // ํํฐ๋งํ ์กฐ๊ฑด์ ์ค์ 4relations?: FindOptionsRelations<Entity> // ๋ถ๋ฌ์ฌ ๊ด๊ณ ํ ์ด๋ธ์ ์ง์ 5order?: FindOneOptionsOrder<Entity> // ์ ๋ ฌ์ ์ง์ 6cache?: boolean | number // ์บ์ฑ ๊ธฐ๊ฐ์ ์ง์ 7}89export interface FindManyOptions<Entity = any> extends FindOneOptions<Entity> {10skip?: number11take?: number12}
- ๋ชจ๋ find ๊ด๋ จ๋ API๋ FindOptions๋ฅผ ์๊ท๋จผํธ๋ก ๋ฐ๋๋ค.
- FindOptions๋ ์ด๋ค ๊ฐ๋ค์ ๋ถ๋ฌ์ฌ์ง ํํฐ๋งํ๋ ์ญํ ์ ํ๋ค.
- FindOptions์ ์ ํํ TS ํ์ ๋ช ์นญ์ FindOneOptions์ FindManyOptions๋ก ์ ์๋ผ์๋ค.
- FindManyOptions๋ FindOneOptions๋ฅผ ์์๋ฐ๊ณ
skip,take2๊ฐ์ง ํ๋กํผํฐ๊ฐ ๋ ์กด์ฌํ๋ค
(1) where ์์ฑ
1// ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ2const users = await userRepository.find({3where: { isActive: true },4})56// ๋ค์ค์กฐ๊ฑด ์ฌ์ฉ๋ฒ7const users = await userRepository.find({8where: [9{ firstName: 'John', lastName: 'Doe' },10{ firstName: 'Jane', lastName: 'Smith' },11],12})1314// ์ค์ฒฉ ์ฌ์ฉ๋ฒ15const users = await userRepository.find({16where: [17isActive: true,18profile : { age: MoreThan(25) },19],20})
(2) order ์์ฑ
1// ๋จ์ผ ์ ๋ ฌ ์ฌ์ฉ๋ฒ2const users = await userRrepository.find({3order: { firstName: 'ASC' },4})56// ๋ณต์ ์ ๋ ฌ ์ฌ์ฉ๋ฒ7const users = await userRrepository.find({8order: { lastName: 'ASC', firstName: 'DESC' },9})
(3) relation ์์ฑ
1const users = await userRepository.find({2relations: ['profile', 'photos'],3})
(4) select ์์ฑ
1const users = await userRepository.find({2select: ['firstName', 'lastName'],3})
(5) cache ์์ฑ
1// ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ2const users = await userRepository.find({3cache: true,4})56// ๊ธฐ๊ฐ ์ง์ ์ ์7const users = await userRepository.find({8cache: 60000, // 60์ด9})
14.7.2 FindManyOptions
1const users = await userRepository.find({2skip: 10, // ์ฒ์ 10๊ฐ๋ฅผ ์ ์ธํ๊ณ ๊ฐ์ ธ์จ๋ค3take: 5, // ์ฒ์ 5๊ฐ๋ง ๊ฐ์ ธ์จ๋ค4})
Skip: ์ ๋ ฌ ํ ์คํตํ ๋ฐ์ดํฐ ๊ฐฏ์๋ฅผ ์ ํ ์ ์๋ค- ๊ธฐ๋ณธ๊ฐ์ 0, 1์ด๋ฉด 1๊ฐ๋ฅผ ์ ์ธํ๊ณ ๊ฐ์ ธ์จ๋ค
Take: ์ฒ์ ๋ช๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ์ง ์ ํ ์ ์๋ค- ๊ธฐ๋ณธ๊ฐ์ 0, 1์ด๋ฉด 1๊ฐ๋ง ๊ฐ์ ธ์จ๋ค
14.7.3 Advanced Options
| ์ฃผ์ ์คํผ๋ ์ดํฐ | ์ฃผ์ ๋ฉ์๋ |
|---|---|
| ๋น๊ต | Equal, Not, LessThan, LessThanOrEqual, MoreThan, MoreThanOrEqual, Between |
| ํจํด๋งค์นญ | Like, ILike |
| ์งํฉ | In, ArrayContains, ArrayContainedBy, ArrayOverlap |
| ๊ธฐํ | IsNull, Or, And, Raw |
(1) Equal ์คํผ๋ ์ดํฐ
1// Equal : ๊ฐ์ ๊ฐ์ ์ฐพ์๋ ์ฌ์ฉ2const users = await userRepository.find({3where: { age: Equal(25) },4})
(2) Not ์คํผ๋ ์ดํฐ
1// Not : ์๋ ๊ฐ์ ์ฐพ์๋ ์ฌ์ฉ2const users = await userRepository.find({3where: { age: Not(25) },4})
(3) LessThan & LessThanOrEqual ์คํผ๋ ์ดํฐ
1// ์ ์๊ฐ & ์ ๊ฑฐ๋ ๊ฐ์ ๊ฐ์ ์ฐพ์๋ ์ฌ์ฉ2const users = await userRepository.find({3where: { age: LessThan(30) },4})5const users = await userRepository.find({6where: { age: LessThanOrEqual(30) },7})
(4) MoreThan & MoreThanOrEqual ์คํผ๋ ์ดํฐ
1// ๋ ํฐ๊ฐ & ํฌ๊ฑฐ๋ ๊ฐ์ ๊ฐ์ ์ฐพ์๋ ์ฌ์ฉ2const users = await userRepository.find({3where: { age: MoreThan(20) },4})5const users = await userRepository.find({6where: { age: MoreThanOrEqual(20) },7})
(5) Between
1// ์ฌ์ด ๊ฐ์ ์ฐพ์๋ ์ฌ์ฉ2const users = await userRepository.find({3where: { age: Between(20, 30) },4})
(6) Like & ILike
1// ์คํธ๋ง์ ๋งค์นญ๋๋ ๊ฐ์ ์ฐพ์๋ ์ฌ์ฉํ๋ค. Like๋ ๋์๋ฌธ์ ๊ตฌ๋ถํ ๋, ILike๋ ๋์๋ฌธ์ ๊ตฌ๋ถํ์ง ์์๋ ์ฌ์ฉ2const users = await userRepository.find({3where: { firstName: Like('Co%') },4})5const users = await userRepository.find({6where: { firstName: ILike('co%') },7})
(7) In
1// ๋ฆฌ์คํธ์ ๋งค์นญ๋๋ ๊ฐ์ ์ฐพ๋๋ค2const users = await userRepository.find({3where: { age: In([20, 25, 30]) },4})
(8) ArrayContains & ArrayContainedBy & ArrayOverlap
1// ArrayContains : ์ํฐํฐ ๋ฆฌ์คํธ ๊ฐ์ด ํ๊ฒ ๋ฆฌ์คํธ์ ์์ ๋๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ํํฐ๋ง2const users = await userRepository.find({3where: { roles: ArrayContains(['admin']) },4})56// ArrayContainedBy : ์ํฐํฐ ๋ฆฌ์คํธ ๊ฐ์ด ํ๊ฒ ๋ฆฌ์คํธ ์์ ๋ชจ๋ ํฌํจ๋๋ ๊ฒฝ์ฐ๋ฅผ ํํฐ๋ง7const users = await userRepository.find({8where: { roles: ArrayContainedBy(['admin', 'user']) },9})1011// ArrayOverlap : ์ํฐํฐ ๋ฆฌ์คํธ ๊ฐ์ด ํ๋๋ผ๋ ํ๊ฒ ๋ฆฌ์คํธ์ ๊ฒน์น๋ ๊ฒฝ์ฐ๋ฅผ ํํฐ๋ง12const users = await userRepository.find({13where: { roles: ArrayOverlap(['admin', 'guest']) },14})
(8-1) ArrayContains
1// Record 12{3id: 1,4tags: ['admin', 'user', 'manager'],5}6// Record 27{8id: 2,9roles: ['user', 'guest'],10}
1const users = await getRepository(User).find({2// Record 2์ Tags๊ฐ ์์ ํ ๊ฐ์ผ๋ Record 2๋ง ๋ฐํ3tags: ArrayContains(['user', 'guest']),4})
(8-2) ArrayContainedBy
1// Record 12{3id: 1,4tags: ['admin', 'user', 'manager'],5}6// Record 27{8id: 2,9roles: ['user', 'guest'],10}
1const users = await getRepository(User).find({2// Record 1๊ณผ Record 2์ Tags๋ค์ด ๋ชจ๋ ํํฐ ๋ฆฌ์คํธ์ ํฌํจ๋๊ธฐ ๋๋ฌธ์ ๋ ๋ค ๋ฐํ3tags: ArrayContainedBy(['admin', 'user', 'guest', 'manager']),4})
(8-3) ArrayOverlap
1// Record 12{3id: 1,4tags: ['admin', 'user', 'manager'],5}6// Record 27{8id: 2,9roles: ['user', 'guest'],10}
1const users = await getRepository(User).find({2// Record 1์ Tags์ Record 2์ Tags๊ฐ ๋ชจ๋ ํํฐ ๋ฆฌ์คํธ์ ๊ฒน์น๋ ๋ถ๋ถ์ด ์๊ธฐ ๋๋ฌธ์ ๋ ๋ค ๋ฐํ3tags: ArrayOverlap(['guest', 'admin']),4})
(9) IsNull
1const users = await userRepository.find({2where: { age: IsNull() }, // age๊ฐ null์ธ ๊ฒฝ์ฐ3})
(10) Or
1const loadedPosts = await dataSource.getRepository(Post).findBy({2// 2๊ฐ์ ์กฐ๊ฑด ์ค ํ๋๋ผ๋ ๋ง์กฑํ๋ฉด ๋ฐํ3title: Or(Equal('About #2'), ILike('About%')),4})
(11) And
1const loadedPosts = await dataSource.getRepository(Post).findBy({2// 2๊ฐ์ ์กฐ๊ฑด ๋ชจ๋ ๋ง์กฑํ๋ฉด ๋ฐํ3title: And(Not(Equal('About #2'), ILike('About%'))),4})
14.7.4 ๋ณต์กํ ๋ ํฌ์งํ ๋ฆฌ ์ฟผ๋ฆฌ
1const users = await userRepository.find({2where: [3{ isActive: true, age: MoreThan(25) },4{ firstName: 'John', age: LessThan(50) },5],6order: { fistName: 'ASC' },7relations: ['profile'],8select: ['firstName', 'lastName'],9skip: 0,10take: 10,11cache: true,12loadRelationIds: true,13loadEagerRelations: false,14withDeleted: false,15})
14.7 exists()
exists(): ํน์ ์กฐ๊ฑด์ Row๊ฐ ์กด์ฌํ๋์ง boolean ๊ฐ์ ๋ฐํ ๋ฐ์ ์ ์๋ค
1const exists = await repository.exists({2where: {3firstName: 'Timber',4},5})
14.8 preload()
- preload()๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ๊ฐ์ Primary Key ๊ธฐ์ค์ผ๋ก ๋ถ๋ฌ์ค๊ณ ์ ๋ ฅ๋ ๊ฐ์ฒด์ ๊ฐ์ผ๋ก ํ๋กํผํฐ๋ฅผ ๋ฎ์ด์ด๋ค
- ๋ฎ์ด์ฐ๋ ๊ณผ์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ฐ์ดํธ ์์ฒญ์ด ๋ณด๋ด์ง์ง๋ ์๋๋ค
1const partiaIUser = {2id: 1,3firstName: 'Timber',4profile: { id: 1 },5}6const user = await repository.preload(partiaIUser)
15. ๊ฐ์ข ํต๊ณ
count(): ํด๋น๋๋ ๊ฐ์๋ฅผ ๋ฐํํ๋คsum(): ํด๋น๋๋ ์์ฑ๋ค์ ๊ฐ์ ์ ๋ถ ํฉ์น ๊ฐ์ ๋ฐํํ๋คaverage(): ํด๋น๋๋ ์์ฑ๋ค์ ํ๊ท ๊ฐ์ ๋ฐํํ๋คminimum(): ํด๋น๋๋ ์์ฑ๋ค์ ์ต์๊ฐ์ ๋ฐํํ๋คmaximum(): ํด๋น๋๋ ์์ฑ๋ค์ ์ต๋๊ฐ์ ๋ฐํํ๋ค
1// firstName์ด Timber์ธ ๊ฐ๋ค์ ๊ฐ์2const count = await repository.count({3where: { firstName: 'Timber' },4})5// age ์ปฌ๋ผ์ firstName์ด Timber์ธ ๊ฐ๋ค์ ํฉ๊ณ6const sum = await repository.sum('age', {7firstName: 'Timber',8})9// age ์ปฌ๋ผ์ firstName์ด Timber์ธ ๊ฐ๋ค์ ํ๊ท ๊ฐ10const average = await repository.average('age', {11firstName: 'Timber',12})13// age ์ปฌ๋ผ์ firstName์ด Timber์ธ ๊ฐ๋ค์ ์ต์๊ฐ14const minimum = await repository.minimum('age', {15firstName: 'Timber',16})17// age ์ปฌ๋ผ์ firstName์ด Timber์ธ ๊ฐ๋ค์ ์ต๋๊ฐ18const maximum = await repository.maximum('age', {19firstName: 'Timber',20})
16. Repository ์ค์ต
user.entity.ts์ count ์ปฌ๋ผ์ ์ถ๊ฐํ๋ค.
1@Column({ default: 0 })2count: number
app ์ปจํธ๋กค๋ฌ์ sample ์์ฒญ์ ๋ง๋ค๊ณ ์ค์ตํ๋ค.
1@Post('sample')2async sample() {3// ๐ (1) ๋ชจ๋ธ์ ํด๋น๋๋ ๊ฐ์ฒด ์์ฑ - ์ ์ฅ์ ์ํจ4const user1 = await this.userRepository.create({5email: 'test@gmail.ai',6})78// ๐ (2) ๋ชจ๋ธ์ ํด๋น๋๋ ๊ฐ์ฒด ์์ฑ - DB์ ์ ์ฅํจ9const user2 = await this.userRepository.save({10email: 'test@gmail.ai',11})1213/*** ๐ (3) preload14* ์ ๋ ฅ๋ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก DB์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ณ ,15* ์ถ๊ฐ์ ๋ ฅ๋ ๊ฐ์ผ๋ก DB์ ๊ฐ์ ธ์จ ๊ฐ๋ค์ ๋์ฒดํจ16* ์ ์ฅํ์ง๋ ์์17*/18const user3 = await this.userRepository.preload({19id: 101,20email: 'test๋ณ๊ฒฝ-์ ์ฅx@gmail.ai',21})2223// ๐ (4) ์ญ์ ํ๊ธฐ24await this.userRepository.delete(101)2526// ๐ (5) ์ซ์ํ ์ปฌ๋ผ ์ฆ๊ฐ (id๊ฐ 1์ธ count ์ปฌ๋ผ์ 2๋งํผ ์ฆ๊ฐ)27await this.userRepository.increment(28{ id: 1 }, //29'count',302,31)3233// ๐ (6) ์ซ์ํ ์ปฌ๋ผ ๊ฐ์ (id๊ฐ 1์ธ count ์ปฌ๋ผ์ 1๋งํผ ๊ฐ์)34await this.userRepository.decrement(35{ id: 1 }, //36'count',371,38)3940// ๐ (7) ๊ฐ์ ์นด์ดํ ํ๊ธฐ41const count = await this.userRepository.count({42where: {43email: ILike('%0%'),44},45})4647// ๐ (8) sum : ์์ฑ๋ค์ ๊ฐ ์ ๋ถ ํฉ์น๊ธฐ48const sum = await this.userRepository.sum('count', {49email: ILike('%0%'),50})5152// ๐ (9) average : ์์ฑ์ ํ๊ท ๊ฐ ๊ตฌํ๊ธฐ53const average = await this.userRepository.average('count', {54id: LessThan(4),55})5657// ๐ (10) min : ์์ฑ์ ์ต์๊ฐ ๊ตฌํ๊ธฐ58const min = await this.userRepository.minimum('count', {59id: LessThan(4),60})6162// ๐ (10) max : ์์ฑ์ ์ต๋๊ฐ ๊ตฌํ๊ธฐ63const max = await this.userRepository.maximum('count', {64id: LessThan(4),65})6667// ๐ (11) find์ findOne๋ ์์(๋ง์ด ๋ค๋ค์ผ๋ ์๋ต)68const userOne = await this.userRepository.findOne({69where: { id: 3 },70})7172// ๐ (12) 3๊ฐ์ ๊ฐ์ ๊ฐ์ ธ์ค๋๋ฐ, ์ ์ฒด ํ ๊ฐ์๋ ๋ฐํํด์ค73const usersAndCount = await this.userRepository.findAndCount({74take: 3,75})7677return usersAndCount78}
17. QueryBuilder
1const users = await userRepository2.craeteQueryBuilder('user') // user ํ ์ด๋ธ์ ๊ธฐ๋ฐ์ผ๋ก QueryBuilder ์์ฑ3.leftJoinAndSelect('user.profile', 'profile') // user.profile ํ ์ด๋ธ์ ์กฐ์ธ4.where('user.isActive = :isActive', { isActive: true }) // isActive๊ฐ true์ธ ๊ฐ๋ง ๊ฐ์ ธ์ค๊ธฐ5// ์ฌ์ฉ๋ฒ : where("์ปฌ๋ผ = :๋ณ์๋ช ", { ๋ณ์๋ช : ๊ฐ })6.orderBy('user.firstName', 'ASC') // firstName์ ๊ธฐ์ค์ผ๋ก ์ค๋ฆ์ฐจ์ ์ ๋ ฌ7.skip(10) // ์ฒ์ 10๊ฐ ์ ์ธํ๊ณ ๊ฐ์ ธ์ค๊ธฐ8.take(5) // ์ฒ์ 5๊ฐ๋ง ๊ฐ์ ธ์ค๊ธฐ9.getMany() // ๊ฒฐ๊ณผ๊ฐ์ ๊ฐ์ ธ์ค๊ธฐ
- ๋ณต์กํ์ง ์์ ์ผ๋ฐ์ ์ธ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ๋๋ Repository๋ฅผ ์ฌ์ฉํ๋๊ฒ ํธ๋ฆฌํ๋ค.
- ์กฐ๊ธ ๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์คํ ํด์ผํ๊ฑฐ๋ ๋ค์ด๋๋ฏนํ๊ฒ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค์ด๊ฐ์ผ ํ ๊ฒฝ์ฐ Query Builder๋ฅผ ์ฌ์ฉํด์ผํ๋ค
17.1 Query Builder์ 5๊ฐ์ง ์คํ ํ์ : (1) select
1const movie = await dataSource2.createQueryBuilder()3.select('movie')4.from(Movie, 'movie') // from(ํ ์ด๋ธ๋ช , '๋ณ์นญ')5.leftJoinAndSelect('movie.detail', 'detail') // leftJoinAndSelect(ํ ์ด๋ธ๋ช .์ปฌ๋ผ๋ช , '๋ณ์นญ')6.leftJoinAndSelect('movie.director', 'director')7.leftJoinAndSelect('movie.genres', 'genres')8.where('movie.id = :id', { id: 1 }) // where(์กฐ๊ฑด, { ๋ณ์๋ช : ๊ฐ }) - ":id"๋ ๋ณ์๋ฅผ ์๋ฏธ9.getOne() // ๊ฒฐ๊ณผ๊ฐ์ ํ๋๋ง ๊ฐ์ ธ์ค๊ธฐ, ์ฌ๋ฌ ๊ฐ๋ getMany()๋ฅผ ์ฌ์ฉ
17.2 Query Builder์ 5๊ฐ์ง ์คํ ํ์ : (2) insert
1await dataSource2.createQueryBuilder()3.insert()4.into(Movie) // data๋ฅผ ๋ฃ์ ํ ์ด๋ธ5.values([6{7title: 'New Movie', //8genre: 'Action',9director: director,10genres: genres,11},12])13.execute() // ์คํ
17.3 Query Builder์ 5๊ฐ์ง ์คํ ํ์ : (3) update
1await dataSource2.createQueryBuilder()3.update(Movie)4.set({ title: 'Updated Movie', genre: 'Drama' })5.where('id = :id', { id: 1 })6.execute()
17.4 Query Builder์ 5๊ฐ์ง ์คํ ํ์ : (4) delete
1await dataSource2.createQueryBuilder()3.delete()4.from(Movie)5.where('id = :id', { id: 1 }) // id๊ฐ 1์ธ ๊ฐ์ ์ญ์ 6.execute()
17.5 Query Builder์ 5๊ฐ์ง ์คํ ํ์ : (5) relations
1const genres = await dataSource2.createQueryBuilder()3.relation(Movie, 'genres') // Movie ํ ์ด๋ธ์ genres ์ปฌ๋ผ์ ๊ธฐ๋ฐ์ผ๋ก4.of(1) // Movie id๊ฐ 1์ธ ๊ฐ์ ๊ฐ์ ธ์ค๊ธฐ5.loadMany() // ๊ฒฐ๊ณผ๊ฐ์ ๊ฐ์ ธ์ค๊ธฐ
17.6 getOne(), getMany(), select()
1// ๋จ์ผ Row๋ง ๊ฐ์ ธ์ฌ ๋2const uesrs = await connection3.getRepository(User) // User ํ ์ด๋ธ์ ๊ธฐ๋ฐ์ผ๋ก Repository ์์ฑ4.createQueryBuilder('user') // createQueryBuilder('๋ณ์นญ')5.select(['user.id', 'user.firstName', 'user.lastName'])6.getOne()78// ๋ณต์ Row๋ง ๊ฐ์ ธ์ฌ ๋9const users = await connection10.getRepository(User)11.craeteQueryBuilder('user')12.select(['user.id', 'user.firstName', 'user.lastName'])13.getMany()
17.7 where()
1// ํ๋์ ํํฐ๋ง ์กฐ๊ฑด ์ ์ฉ2const users = await connection3.getRepository(User) // User ํ ์ด๋ธ์ ๊ธฐ๋ฐ์ผ๋ก Repository ์์ฑ4.createQueryBuilder('user') // user ํ ์ด๋ธ์ ๊ธฐ๋ฐ์ผ๋ก QueryBuilder ์์ฑ5.where('user.isActive = :isActive', { isActive: true })6.getMany()78// ๋ค์์ ํํฐ๋ง ์กฐ๊ฑด ์ ์ฉ9const users = await connection10.getRepository(User)11.createQueryBuilder('user')12.where('user.firstName = :firstName', { firstName: 'Timber' })13.andWhere('user.lastName = :lastName', { lastName: 'Saw' })14.getMany()
17.8 orderBy()
1const users = await connection2.getRepository(User)3.createQueryBuilder('user')4.orderBy('user.lastName', 'ASC') // lastName์ ๊ธฐ์ค์ผ๋ก ์ค๋ฆ์ฐจ์ ์ ๋ ฌ5.addOrderBy('user.firstName', 'DESC') // firstName์ ๊ธฐ์ค์ผ๋ก ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ6.getMany()
17.9 skip(), take()
1const users = await connection2.getRepository(User)3.createQueryBuilder('user')4.skip(10) // ์ฒ์ 10๊ฐ ์ ์ธํ๊ณ ๊ฐ์ ธ์ค๊ธฐ5.take(5) // ์ฒ์ 5๊ฐ๋ง ๊ฐ์ ธ์ค๊ธฐ6.getMany()
17.10 join()
1// Inner Join2const users = await connection3.getRepository(User)4.createQueryBuilder('user') // createQueryBuilder('๋ณ์นญ')5.innerJoinAndSelect('user.profile', 'profile') // innerJoinAndSelect(ํ ์ด๋ธ๋ช .์ปฌ๋ผ๋ช , '๋ณ์นญ')6.getMany()78// Left Join9const users = await connection10.getRepository(User)11.createQueryBuilder('user')12.leftJoinAndSelect('user.photos', 'photos') // leftJoinAndSelect(ํ ์ด๋ธ๋ช .์ปฌ๋ผ๋ช , '๋ณ์นญ')13.getMany()
17.11 Aggregation(์ง๊ณ)
1const userCount = await connection2.getRepository(User)3.creawteQueryBuilder('user')4.select('COUNT(user.id)', 'count') // select('์ง๊ณํจ์(ํ ์ด๋ธ๋ช .์ปฌ๋ผ๋ช )', '๋ณ์นญ')5.getRawOne()
17.12 Subquery
1const users = await connection2.getRepository(User)3.createQueryBuilder('user')4.where((qb) => {5const subQuery = qb6.subQuery()7.select('subUser.id')8.from(User, 'subUser')9.where('subUser.isActive = :isActive', { isActive: true })10.getQuery()11return 'user.id IN ' + subQuery // user.id๊ฐ subQuery์ ํฌํจ๋๋ ๊ฐ๋ง ๊ฐ์ ธ์ค๊ธฐ12})13.setParameters('isActive', true)14.getMany()
17.12 Raw Query
1const users = await connection2.getRepository(User)3.createQueryBuilder()4.select('user')5.from(User, 'user')6.where('user.isActive = :isActive', { isActive: true })7.getRawMany()