[TypeORM] `save()` 사용시 유의할점

Falcon·2022년 8월 2일
2

🔒 문제상황

TypeORM save() 메소드의 설명을 읽어보면

Saves all given entities in the database. If entities do not exist in the database then inserts, otherwise updates.

이미 레코드가 존재하면 UPDATE, 존재하지 않으면 INSERT
근데 만약 테이블이 다음처럼 설계 되어 있는 경우, 'UPDATE'를 예상한 상황에서도 'INSERT' 가 일어날 수 있다.

예제 코드

const userRepository = await this.orm.getRepository(User);
// ⚠️ PK `id` 만 가지고 record 하나를 특정할 수 없다.
const user = await userRepository.findOne({
  where: {id}
});

console.dir(user);

const entries = Object.entries(request.body);

for (const [key, value] of entries) {
  user[key] = value;
}

user.save();

쿼리 결과

문제의 원인

TypeORM save() 메소드는 물려있는 모든 PKWHERE 절에 담아 SELECT 쿼리를 일단 실행한다 => PK가 하나라도 누락될 경우 정상적으로 SELECT 결과를 받아오지 못해 INSERT 가 발생할 수 있다.

ORM 으로부터 생성된 쿼리 전문

그럼 뭘 써야하나?

1. .update()

// 간단한 쿼리에서 가장 효율적인 방법이다.
await userRepository.update({id}, request.body);

2. QueryBuilder

쿼리가 좀 복잡해질 경우 쿼리빌더를 사용하는게 좋다.

const updatedUser = await this.orm.createQueryBuilder()
                        .update(User)
                        .set(request.body)
                        .where({id})
                        .returning("*")
                        .execute()
                        .then(({raw})=>raw[0]);

📝 결론

PK 가 여러개 물려있는 상태에서 upsert 를 하고싶다면
.save() 메소드 대신 다음 3가지 중 1개의 방법을 사용해야 한다.

  • .update() 사용
  • QueryBuilder 로 쿼리 작성
  • 모든 PK 값을 where 절에 명시 => 유일함을 보장하도록

🔗 Reference

profile
I'm still hungry

0개의 댓글