- Published on
typeorm repository 에 대해서
- Authors
- Name
- 길재훈
TypeORM Repository
에 대해서 알아본다.
TypeORM
은 CRUD
등등.. DML
관련 부분을 처리해주는 Collection
을 제공한다.
이러한 Collection
은 3가지로 나누는데, EntityManager
, Repository
, QueryBuilderer
이다.
여기서는 Repository
먼저 보고 이후 EntityManager
, QueryBuilder
관련API
를 살펴보도록 할것이다.
Repository
를 사용하기 위해서는 DataSource
에서 getRepository
를 사용하여 가져올 수 있다.
Repository
는 EntityManager
와 비슷하지만 다르게 Entity
위주의 세부적인 조작을 위해서 만들어졌다고 생각하면 된다.
getRepository
를 통한query
import { User } from "./entity/User"
const userRepository = dataSource.getRepository(User)
const user = await userRepository.findOneBy({
id: 1,
})
user.name = "Umed"
await userRepository.save(user)
Repository
는 총 3개의 Type
이 존재한다.
Repository
모든entity
에 대한 일반적인 저장소TreeRepository
Tree-Entities
로 부터 사용된 저장소의extensions
이다tree structures
를 위한 특별한methods
를 가진다.MongoRepository
오직MongoDB
를 지원하기 위해 사용되기 위한 특별한 기능들을 가진 저장소이다.
Find Optopns
모든 repository
그리고 manager
의 .find*
메서드들은 특별한 options
를 허용한다.
이로써 QueryBuilder
사용 없이 필요한 data
를 query
하는데 사용할 수 있다.
select
기본 Object
의 어떤 속성을 선택해야 하는지를 나타낸다
userRepository.find({
select: {
firstName: true,
lastName: true,
}
})
위는 다음과 같다.
SELECT "firstName", "lastName" FROM "user";
relations
기본 entity
와 함께 load
되는데 필요한 관계를 정의한다.sub-relations
(하위관계) 도 load
할수 있다.
userRepository.find({
relations: {
profile: true,
photos: true,
videos: true,
}
});
userRepository.find({
relations: {
profile: true,
photos: true,
videos: {
videoAttributes: true,
}
}
});
위는 아래의 SQL
문과 같다
SELECT * FROM "user" as "user"
LEFT JOIN "profile" as "profile"
ON "profile"."id" = "user"."profileId"
LEFT JOIN "photos" as "photos"
ON "photos"."id" = "user"."photosId"
LEFT JOIN "videos" as "videos"
ON "videos"."id" = "user"."vidoesId"
SELECT * FROM "user" as "user"
LEFT JOIN "profile" as "profile"
ON "profile"."id" = "user"."profileId"
LEFT JOIN "photos" as "photos"
ON "photos"."id" = "user"."photosId"
LEFT JOIN "videos" as "videos"
ON "videos"."id" = "user"."videosId"
LEFT JOIN "videos_attributes" as "videos_attributes"
ON "videos_attributes"."id" = "videos"."videos_attributesId"
위를 보면 Camecase
시 구분자는 _
로 구분되는듯 하다.
where
entity
의 쿼리에 대한 간단한 조건이라고 보면된다.
userRepository.find({
where: {
firstName: "Timber",
lastName: "Saw",
},
})
이는 다음의 SQL
과 일치하다
SELECT * FROM User WHERE firstName = "Timber" AND lastName = "Saw";
query
시 Embadded Entity
같은경우 다음처럼 가능하다.
userRepository.find({
relations: {
project: true,
},
where: {
project: {
name: "TypeORM",
initials: "TROM",
}
},
})
이는 다음과 같다
SELECT * FROM "user" as "user"
LEFT JOIN "project" as "project"
ON "project"."id" = "user"."projectId"
WHERE "project"."name" = "TypeORM" AND "project"."initials" = "TROM";
또한 where
문에서 배열로 담아 쿼리하면 OR
과 같은 방식으로 쿼리할 수 있다.
userRepository.find({
where: [
{ firstName: "Timber", lastName: "Saw" },
{ firstName: "Stan", lastName: "Lee" },
],
})
이는 다음과 같다
SELECT * FROM "user" WHERE
("firstName" = "Timber" AND "lastName" = "Saw")
OR
("firstName" = "Stan" AND "lastName" = "Lee");
order
오름차순, 내림차순을 지정한다.
userRepository.find({
order: {
name: "ASC", // 오름차
id: "DESC", // 내림차
}
})
이는 다음과 같다.
SELECT * FROM "user"
ORDER BY "name" ASC, "id" DESC
withDeleted
이는 softDelete
혹은 softRemove
로 삭제된 entity
를 포함한다. 사용하려면 @DeleteDateColumn
을 포함해야만 한다고 한다.
기본적으로 일시 삭제된 Entity
는 포함하지 않는다
userRepository.find({
withDeleted: true,
})
다음의 옵션들은
find*
메서드들 즉,find
,findBy
,findAndCount
,findAndCountBy
처럼 여러Entity
를 반환할때 사용가능한 옵션들이다.
skip
entity
들을 가져오는 곳 에대한 offset
을 지정한다
userRepository.find({
skip: 5
})
이는 다음과 같다.
SELECT * FROM "user" OFFSET 5;
take
가져올 entity
의 최대갯수를 제한한다.
userRepository.find({
take: 10,
})
sql
문은 다음과 같다
SELECT * FROM "user" LIMIT 10;
보통 skip
과 take
는 같이 사용한다.
userRepository.find({
order: {
columnName: "ASC",
},
skip: 0,
take: 10,
})
이는 0
개부터 10
개까지 오름차순
으로 가져오는 로직이다.
SELECT * FROM "user"
ORDER BY "columnName" ASC
LIMIT 10
OFFSET 0
cache
결과를 chache
한다. 자세한건 caching 에서 살펴보라고 한다.
userRepository.find({
cache: true,
})
lock
query
에 대한 mechanism
에 대한 locking
을 활성화한다. 이는 오직 finxOne
그리고 findOneBy
메서드에 사용가능하다.
다음처럼 사용가능하다.
{ mode: "optimistic", version: number | Date }
혹은
{
mode: "pessimistic_read" |
"pessimistic_write" |
"dirty_read" |
/*
"pessimistic_partial_write" and "pessimistic_write_or_fail" are deprecated and
will be removed in a future version.
Use onLocked instead.
*/
"pessimistic_partial_write" |
"pessimistic_write_or_fail" |
"for_no_key_update" |
"for_key_share",
tables: string[],
onLocked: "nowait" | "skip_locked"
}
다음의 코드를 보자
userRepository.findOne({
where: {
id: 1,
},
lock: { mode: "optimistic", version: 1 },
})
lock
에 관련된 모드들에 대해서는 아직 모르는것이 많다. 이부분에 대해서 살펴보려면 다음의 lock mode 에서 보도록 하자.
Advanced options
TypeORM
은 많은 built-in
연산자들을 제공한다.
Not
import { Not } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: Not("About #1"),
})
이는 다음과 같다
SELECT * FROM "post" WHERE "title" != 'About #1'
LessThan
import { LessThan } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
likes: LessThan(10),
})
이는 다음과 같다
SELECT * FROM "posts" WHERE "likes" < 10;
LessThanOrEqual
import { LessThan } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
likes: LessThanOrEqual(10),
})
이는 다음과 같다
SELECT * FROM "posts" WHERE "likes" <= 10;
MoreThan
import { MoreThan } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
likes: MoreThan(10),
})
SELECT * FROM "post" WHERE "likes" > 10
MoreThanOrEqual
import { MoreThanOrEqual } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
likes: MoreThanOrEqual(10),
})
SELECT * FROM "post" WHERE "likes" >= 10
Equal
import { Equal } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: Equal("About #2"),
})
SELECT * FROM "post" WHERE "title" = 'About #2'
like
import { Like } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: Like("%out #%"),
})
SELECT * FROM "post" WHERE "title" LIKE '%out #%'
ILike
import { ILike } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: ILike("%out #%"),
})
SELECT * FROM "post" WHERE "title" ILIKE '%out #%'
Between
import { Between } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
likes: Between(1, 10),
})
SELECT * FROM "post" WHERE "likes" BETWEEN 1 AND 10
In
import { In } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: In(["About #2", "About #3"]),
})
SELECT * FROM "post" WHERE "title" IN ('About #2','About #3')
Any
이는
Postgres
문법이라고 한다.
import { Any } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: Any(["About #2", "About #3"]),
})
SELECT * FROM "post" WHERE "title" = ANY(['About #2','About #3'])
IsNull
import { IsNull } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: IsNull(),
})
SELECT * FROM "post" where "title" IS NULL;
ArrayContains
사실 이 문법은 처음본다...
Operators 에도 없어보이는데... 어디 문법이지?
import { ArrayContains } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
categories: ArrayContains(["TypeScript"]),
})
SELECT * FROM "post" WHERE "categories" @> '{TypeScript}'
이 밑의 구문들은 아직 이해가 기지 않는 구문들이다. 일단 넘긴다.
Custom Repository
Repository
사용시, Custom Method
작성이 가능하다. 다음을 보자
// user.repository.ts
export const UserRepository = dataSource.getRepository(User).extend({
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany()
},
})
// user.controller.ts
export class UserController {
users() {
return UserRepository.findByName("Timber", "Saw")
}
}
위를 보면 extend
를 사용하여 method
를 등록하고, 사용하는것을 볼수 있다.
마무리
이렇게, Repository
관련 내용을 살펴보았다.
보면서 SQL
관련 공부도 더러 되는것 같아 많은 이해가 쌓인 느낌이다.
다음은 EntityManager
에 대해서 살펴보도록 한다.