본문 바로가기
Backend/TypeORM

save 와 upsert

by 찬찬2 2023. 4. 25.

MySQL로 작업을 하던 시절 데이터베이스에 데이터를 저장시키기 위해 SQL 키워드 "INSERT"를 사용했다. 그리고 상황에 따라 데이터가 있으면 UPDATE, 없으면 INSERT하는 UPSERT에 대한 개념을 알게되었다.

 

새로운 직장에서 typeORM을 공부하며 CRUD를 구현하던 중 데이터를 저장하는 상황에서 나는 단순히 INSERT를 먼저 생각하게 되었고, 공식문서에서 Repository API 목록을 살펴보니 INSERT를 실행시킬 수 있는 API가 세 가지가 있더라...

 

① save ② insert ③ update ④ upsert

 

공식문서에서 정의하는 내용을 살펴 보면...

 

① save

Saves a given entity or array of entities. If the entity already exist in the database, it is updated. If the entity does not exist in the database, it is inserted. It saves all given entities in a single transaction (in the case of entity, manager is not transactional). Also supports partial updating since all undefined properties are skipped. Returns the saved entity/entities.

 

즉, 데이터가 있으면 UPDATE, 없으면 INSERT 한다는 말.

 

실제로 쿼리를 돌려봤을때 만약 데이터베이스에 데이터가 없으면 INSERT INTO- 가 실행되고, 있으면 SELECT절로 조회를 거친 후 UPDATE를 하게되면서 두 개의 쿼리문이 실행되더라.

 

그렇다면 있고 없고를 판단할때 무엇을 기준으로 SELECT를 할까? 바로 PK이다. 만약 PK값이 null 이면 INSERT, 있으면 UPDATE인것이다.

 

④ upsert

 Inserts a new entity or array of entities unless they already exist in which case they are updated instead.

 

해석하자면 save와 마찬가지로 데이터베이스에 데이터가 있으면 update, 없으면 insert 한다는 것.

 

위 메서드의 이름만 보더라도 기존에 내가 알고 있던 UPSERT와 일치하기 때문에 당연히 이거겠지 했다. 하지만 UPDATE는 커녕 계속 새로운 row만 만들더라.

VSC에서 실행된 쿼리문을 보면, Entity 클래스에서 설정한 PrimaryGeneratedColumn 데코레이터의 컬럼만 제외시키고 쿼리가 만들어지고 있었다. 그러니까 INSERT VALUES에 포함시키지 않는다는 말. (참고링크)

 

(Column 을 PrimaryGeneratedColumn 로 만들지 않고, 기본 Column 데코레이터에 옵션 'unique' 를 true 로 해줘야 한다. 이렇게 하면 upsert 는 내가 원하는데로 없으면 insert, 있으면 update 가 된다. 하지만, PrimaryGeneratedColumn 를 사용하지 못함으로써 index 값을 직접 넣어줘야 하는 번거로움이 있다.)

 

SELECT를 거치지 않고 "INSERT INTO (컬럼1, 컬럼2) VALUES (값1, 값2) ON CONFLICT (비교대상 컬럼) DO UPDATE SET-"와 같이 어떻게 실행시킬 수 있을까? QueryBuilder로 orUpdate 메서드를 사용해서 쿼리문을 조금 더 구제적으로 표현하면 된다더라.

 

 

댓글