TypeORM
한글로 번역된 사이트를 보고, 정리한 글입니다. https://orkhan.gitbook.io/typeorm/readme_ko
사용방법이 변경되어 타입오알엠 공식페이지도 꼭 확인해야합니다. https://typeorm.io/
typeorm
🧐 프로젝트 시작하기
1. 설치 명령어
1
npm install typeorm reflect-metadata
2. app.ts에 모듈을 등록
1
import "reflect-metadata";
3. DB 드라이버 설치 - mariaDB 또는 mysql
1
npm install mysql2
4. 타입스크립트 환경 설정
- 3.3 버전 이상
- tsconfig.json에 다음 설정 추가
1
2
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
5. data-source.ts
파일에 데이터베이스와 연결하는 코드 작성하기
1
2
3
4
5
6
7
8
9
10
11
12
13
export const AppDataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
username: "test",
password: "test",
database: "test",
synchronize: true,
logging: true,
entities: [Post, Category],
subscribers: [],
migrations: []
});
🧐 디렉토리 구조
1
2
3
4
5
6
7
8
9
10
11
MyProject
├── src
│ ├── entity // 모델 저장
│ │ └── User.ts // 샘플 모델
│ ├── migration // 마이그레이션 파일 저장
│ ├── data-source.ts // ORM과 database을 연결하는 코드 작성
│ └── index.ts // 애플리케이션 시작점
├── .gitignore // gitignore file
├── package.json
├── README.md
└── tsconfig.json
🧐 데이터베이스와 연결하기
createConnection을 사용하여 데이터베이스와 연결할 수 있다. 주로 index.ts
또는 app.ts
에 작성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import "reflect-metadata";
import { createConnection } from "typeorm";
import { Photo } from "./entity/Photo";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "test",
entities: [Photo],
synchronize: true,
logging: false
})
.then((connection) => {
// here you can start to work with your entities
})
.catch((error) => console.log(error));
✅type
type에는 사용한 데이터베이스를 지정한다.
- mysql, mariadb, postgres, cockroachdb, sqlite, mssql, oracle, cordova, nativescript, react-native, expo, or mongodb
✅entities
entities 목록이며, 사용할 엔터티들을 추가해야한다.
- 매번 엔터티를 설정에 추가하기는 불편하기 때문에 다음과 같은 코드를 작성하여, 모든 엔터티를 불러올 수 있다.
1
2
3
entities: [
__dirname + "/entity/*.js"
],
✅synchronize
synchronize를 true로 설정해두면, 애플리케이션이 실행될 때마다 엔터티가 데이터베이스와 동기화된다.
🧐 테이블 생성하기
타입오알엠에서의 모델은 데이터베이스의 테이블이다. 모델을 통해서 데이터베이스 테이블을 생성할 수 있다.
✅@Entity
를 사용하여 모델 작성하기
1
2
3
4
5
6
7
8
9
10
11
import { Entity } from "typeorm";
@Entity()
export class Photo {
id: number;
name: string;
description: string;
filename: string;
views: number;
isPublished: boolean;
}
✅@Column
를 사용하여 테이블 열 추가하기
상단의 @Entity
를 사용하여 만든 모델을 보면, 테이블에 열이 존재하지 않는다. @Column
를 사용하여 열을 추가할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Entity, Column } from "typeorm";
@Entity()
export class Photo {
@Column()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
@Column()
에 옵션 값을 입력할 수도 있다.
@예시 1
1
@Column({nullable:true,unique:true})
위의 코드는 다음과 같은 옵션을 지정한 것이다.
- null을 허용하는지 결정하는
nullable
- 유일한 값을 의미하는
unique
@예시 2
1
@Column({ length: 15, nullable: true, comment: '전화번호' })
위의 코드는 다음과 같은 옵션을 지정한 것이다.
- null을 허용하는지 결정하는
nullable
- 전체 길이를 지정하는
length
- 칼럼에 대한 설명을 작성한
comment
✅ @PrimaryColumn
를 사용하여 기본 열 생성하기
우리는 @Column
을 사용하여 테이블에 열을 추가해주었다. 하지만 테이블에는 기본 키가 있는 열이 꼭 존재해야한다. 이는 @PrimaryColumn
하여 기본 열을 지정해줄 수 있다.
다음은 id 열을 기본 열로 생성한 코드 예시이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Entity, Column, PrimaryColumn } from "typeorm";
@Entity()
export class Photo {
@PrimaryColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
✅@PrimaryGeneratedColumn
를 사용하여 기본 열 생성하기
@PrimaryColumn
을 사용하여 기본 열을 설정해주었다. 하지만 id는 자동으로 생성(자동으로 증가)되는 경우가 많다. 이렇게 자동 생성을 하기 위해서는 @PrimaryGeneratedColumn
을 사용해야한다.
다음은 @PrimaryGeneratedColumn
을 사용하여 id가 자동으로 생성되게 하는 코드 예시이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
🧐 데이터베이스 관계 생성하기
✅@OneToOne
@OneToOne
을 사용하면 1:1 관계를 만들 수 있으며, type => 엔터티 클래스
를 사용하여 관계를 만들고자 하는 엔터티의 클래스를 명시해주어야한다.
관계를 소유하는 쪽에 @JoinColumn
를 작성하여 관계를 소유하고 있음을 명시해주어야한다.
예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import {
Entity,
Column,
PrimaryGeneratedColumn,
OneToOne,
JoinColumn
} from "typeorm";
import { Photo } from "./Photo";
@Entity()
export class PhotoMetadata {
@PrimaryGeneratedColumn()
id: number;
@Column("int")
height: number;
@Column("int")
width: number;
@OneToOne((type) => Photo)
@JoinColumn()
photo: Photo;
}
✅@OneToMany
/@ManyToOne
@OneToMany
은 1:N의 관계(일대다)를 생성할 때 사용하고, @ManyToOne
은 N:1 관계(다대일)를 생성할 때 사용한다. @OneToMany
는 @ManyToOne
없이는 존재할 수 없습니다. 이 관계에서 소유자는 항상 다대일(@ManyToOne
)입니다. @ManyToOne
사용하는 객체가 관련 객체의 id를 저장한다는 의미입니다.
예를 들어, 하나의 주문이 여러개의 포함할 수 있습니다. 여기서 주문은 1이고, 제품은 N이 됩니다. 이 관계를 구현할 때 주문 엔터티에는 여러 제품이 있으므로 주문 엔터티가 “다” 이며, 이것이 일대다 관계(@OneToMany
)입니다. 그리고 제품 엔터티에는 주문과 관련된 주문 id가 있으므로, 여러 제품이 하나의 주문에 속할 수 있습니다. 이것이 다대일 관계(@ManyToOne
)입니다.
#주문 엔터티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Order.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { Product } from "./Product";
@Entity()
export class Order {
@PrimaryGeneratedColumn()
id: number;
@Column()
orderName: string;
@OneToMany(() => Product, (product) => product.order)
products: Product[];
}
#제품 엔터티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Product.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
import { Order } from "./Order";
@Entity()
export class Product {
@PrimaryGeneratedColumn()
id: number;
@Column()
productName: string;
@ManyToOne(() => Order, (order) => order.products)
order: Order;
}
🧐 데이터베이스에 데이터 추가하기
index.ts
를 실행하면 자동으로 데이터베이스와의 연결이 초기화가 되며, 테이블이 생성된다.
save
를 사용하여 데이터를 추가할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";
const photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
await AppDataSource.manager.save(photo);
console.log("Photo has been saved. Photo id is", photo.id);
🧐 데이터베이스에 데이터 조회하기
find()
를 사용하여 여러개의 데이터를 조회하고, findOne()
을 사용하여 특정 데이터를 조회한다.
예전 메소드
find()
: 전체 조회find({조건})
: 조건에 맞는 모든 데이터 조회findOne(id)
: 아이디값을 사용하여 데이터 조회findOne({조건})
: 조건에 맞는 데이터 조회
변경된 메소드
find()
: 전체 조회findBy({조건})
: 조건에 맞는 모든 데이터 조회findOneBy({조건})
: : 조건에 맞는 데이터 조회findAndCount()
: 모든 데이터를 조회하고, 데이터의 갯수 반환
예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";
const photoRepository = AppDataSource.getRepository(Photo);
const allPhotos = await photoRepository.find();
console.log("All photos from the db: ", allPhotos);
const firstPhoto = await photoRepository.findOneBy({
id: 1
});
console.log("First photo from the db: ", firstPhoto);
const meAndBearsPhoto = await photoRepository.findOneBy({
name: "Me and Bears"
});
console.log("Me and Bears photo from the db: ", meAndBearsPhoto);
const allViewedPhotos = await photoRepository.findBy({ views: 1 });
console.log("All viewed photos: ", allViewedPhotos);
const allPublishedPhotos = await photoRepository.findBy({ isPublished: true });
console.log("All published photos: ", allPublishedPhotos);
const [photos, photosCount] = await photoRepository.findAndCount();
console.log("All photos: ", photos);
console.log("Photos count: ", photosCount);
🧐 데이터베이스에 데이터 업데이트하기
findOneBy
을 사용하여 업데이트할 데이터를 찾고, save
를 사용하여 데이터를 업데이트한다.
예시 코드
1
2
3
4
5
6
7
8
9
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";
const photoRepository = AppDataSource.getRepository(Photo);
const photoToUpdate = await photoRepository.findOneBy({
id: 1
});
photoToUpdate.name = "Me, my friends and polar bears";
await photoRepository.save(photoToUpdate);
🧐 데이터베이스에 데이터 삭제
findOneBy
을 사용하여 삭제할 데이터를 찾고, remove
를 사용하여 데이터를 삭제한다.
예시 코드
1
2
3
4
5
6
7
8
import { Photo } from "./entity/Photo";
import { AppDataSource } from "./index";
const photoRepository = AppDataSource.getRepository(Photo);
const photoToRemove = await photoRepository.findOneBy({
id: 1
});
await photoRepository.remove(photoToRemove);