엔터티 매니저로 모든 엔터티에 대해서 CRUD 작업 가능
엔터티 매니저는 엔터티 레포지토리를 한 곳에 모아 놓은 장소와 같다.
데이터소스를 통해 엔터티 매니저에 접근할 수 있음.
import { DataSource } from "typeorm"
import { User } from "./entity/User"
const myDataSource = new DataSource(/*...*/)
const user = await myDataSource.manager.findOneBy(User, {
id: 1,
})
user.name = "Umed"
await myDataSource.manager.save(user)
레포지토리는 엔터티 매니저와 비슷한다. 하지만 이건 제한된 구체적인 (특정) 엔터티에만 작동함.
엔터티 매니저를 통해 레포지토리에 접근할 수 있음.
import { User } from "./entity/User"
const userRepository = dataSource.getRepository(User)
const user = await userRepository.findOneBy({
id: 1,
})
user.name = "Umed"
await userRepository.save(user)
userRepository.find({
select: {
firstName: true,
lastName: true,
},
})
위 코드는 아래 쿼리로 작동함.
SELECT "firstName", "lastName" FROM "user"
메인 엔터티에서 로드하고 서브 엔터티도 같이 로드 가능
userRepository.find({
relations: {
profile: true,
photos: true,
videos: true,
},
})
userRepository.find({
relations: {
profile: true,
photos: true,
videos: {
videoAttributes: true,
},
},
})
위 코드는 아래 쿼리로 작동함.
SELECT * FROM "user"
LEFT JOIN "profile" ON "profile"."id" = "user"."profileId"
LEFT JOIN "photos" ON "photos"."id" = "user"."photoId"
LEFT JOIN "videos" ON "videos"."id" = "user"."videoId"
SELECT * FROM "user"
LEFT JOIN "profile" ON "profile"."id" = "user"."profileId"
LEFT JOIN "photos" ON "photos"."id" = "user"."photoId"
LEFT JOIN "videos" ON "videos"."id" = "user"."videoId"
LEFT JOIN "video_attributes" ON "video_attributes"."id" = "videos"."video_attributesId
userRepository.find({
where: {
firstName: "Timber",
lastName: "Saw",
},
})
쿼리
SELECT * FROM "user"
WHERE "firstName" = 'Timber' AND "lastName" = 'Saw'
이런 식으로 아래 항목 작동함.
핵심은 sql 쿼리로 날릴 수 있는 대부분을 엔터티 매니저(또는 레포지토리)를 통해 DB에 쿼리를 보낼 수 있음.(정확히는 sql로 변환)
비교는 이런 식으로
import { LessThanOrEqual } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
likes: LessThanOrEqual(10),
})
Raw 함수는 TypeORM에서 SQL의 원시(raw) 쿼리 부분을 직접적으로 작성할 수 있게 해주는 기능
find 옵션은 자주 쓰면서 숙지하면 좋을 듯!
https://typeorm.io/find-options
오호 이런 식으로 커스텀 레포지토리 만들 수 있다!
// 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")
}
}
await connection.transaction(async (manager) => {
// in transactions you MUST use manager instance provided by a transaction,
// you cannot use global entity managers or repositories,
// because this manager is exclusive and transactional
const userRepository = manager.withRepository(UserRepository)
await userRepository.createAndSave("Timber", "Saw")
const timber = await userRepository.findByName("Timber", "Saw")
})
트랜잭션은 scope를 지닌 다는 것 기억하기!
쿼리 러너(Query Runner): 데이터베이스 쿼리를 실행하는 데 사용되는 객체나 인터페이스
엔터티 매니저는 내부적으로 쿼리 러너를 통해 DB와 통신한다.
근데 엔터티 매니저를 통하지 않고 개발자가 직접 쿼리 러너 인스턴스로 저수준에서 DB에 쿼리를 보낼 수 있다.
이거랑 똑같다!
const user = manager.create(User) // same as const user = new User();
create는 우선 인스턴스 생성하고 save를 호출할 때 없으면 insert, 있으면 update
insert는 그냥 바로 생성
전자는 엔터티 생성 후 추가적인 처리 및 검증 로직 적용 쉬움
insert는 빨리 추가할 수 있는데 성능도 더 좋긴 함.
근데 엔티티가 이미 존재하면 오류발생할 수 있고, 검증 로직 적용이 어려움.
TypeORM에서 중심이 되는 객체
애플리케이션과 데이터베이스 간의 연결을 관리
얘가 EntityManager, Repository를 생성하고 제공하는 것
개별 데이터베이스 트랜잭션을 관리하는 객체
모든 데이터베이스 CRUD 작업은 EntityManager를 통해 수행됨
DataSource에서 생성됨
특정 엔티티 타입에 대한 데이터베이스 작업을 더 쉽게 처리하기 위해 추상화된 API를 제공
각 엔티티에 대해 하나의 Repository가 존재하며,
이건 EntityManager를 통해 생성됨
얘는 엔터티에 대한 CRUD 작업을 간소화하고, 엔터티 특정 로직을 캡슐화
EntityManager의 다른 표현
Repository의 EntityManager 인스턴스를 참조할 때 사용함.
manager - The EntityManager used by this repository.
QueryRunner는 데이터베이스 연결과 트랜잭션 관리를 더 세밀하게 제어할 수 있게 해주며, 복잡한 트랜잭션 시나리오 또는 여러 개의 리포지토리 간 트랜잭션을 관리할 때 주로 사용됩니다.
import { DataSource } from "typeorm";
// 데이터 소스를 설정합니다.
const dataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "your_username",
password: "your_password",
database: "your_database"
});
async function runDatabaseOperations() {
// QueryRunner 생성
const queryRunner = dataSource.createQueryRunner();
// 연결 시작
await queryRunner.connect();
// 트랜잭션 시작
await queryRunner.startTransaction();
try {
// 데이터베이스 작업 수행, 예를 들어 엔터티 저장
const user = queryRunner.manager.create("User", { firstName: "John", lastName: "Doe" });
await queryRunner.manager.save(user);
// 더 복잡한 쿼리를 직접 실행할 수도 있습니다.
await queryRunner.query(`UPDATE users SET firstName = 'Jane' WHERE lastName = 'Doe'`);
// 트랜잭션 커밋
await queryRunner.commitTransaction();
} catch (error) {
// 에러 발생 시 롤백
await queryRunner.rollbackTransaction();
console.error("Transaction rolled back because of:", error);
} finally {
// 연결 해제
await queryRunner.release();
}
}
// 함수 실행
runDatabaseOperations();
