home
Sqlalchemy의 refresh, merge
Session.refresh() 주요 용도
세션 내 객체를 데이터베이스에서 최신 상태로 다시 불러옵니다. 주로 다른 세션/트랜잭션에서 변경된 데이터를 반영할 때 사용하며, expire()와 달리 즉시 SELECT 쿼리를 실행합니다.
flush()와 refresh() 관계
- 같은 세션/트랜잭션:
flush()만으로 충분. 변경사항이 메모리에 반영되고 autoflush로 쿼리 시 자동 처리됩니다. - 다른 세션:
flush()후에도 변경이 보이지 않습니다.commit()후refresh()가 필요합니다.
Bulk Update 특성
| 상황 | 세션 추적 | 객체 상태 | refresh 필요 |
|---|---|---|---|
obj.status = value |
O | 메모리 즉시 반영 | ❌ 불필요 |
query().filter(...).update({}) |
X (직접 SQL) | stale 상태 | ✅ 필수 |
실전 워크플로우 예시
1. 같은 세션: flush만
obj.name = 'updated'
session.flush() # DB 버퍼 전송, 객체는 이미 최신
# 2. 다른 세션 동기화
session2.refresh(obj) # commit된 DB 값으로 갱신
# 3. Bulk update 후
session.query(Model).filter(...).update({"status": "done"})
session.refresh(existing_obj) # 기존 객체 동기화
근데 refresh는 select를 한번 강제로 불러오기 때문에.. 업데이트 칠 때는 flush만 쓰는게 좋지 않나 싶어요.
그리고, refresh가 필요 없는 필드는 그냥 flush 만으로도 충분합니다. 단지, 예전 데이터를 보고 있을 뿐.. 근데 특정 필드만 업데이트를 할 수도 있어요.expire(['specific_attr'])를 사용하면 됩니다.
근데 이러면 너무 관리가 힘들어질 것 같지 않나요? 업데이트 이후에 싱싱한 객체를 보려면 그냥 refresh 한번 치는 게 좋아보입니다.
그럼 merge는요?
SQLAlchemy의 session.merge()는 detached(세션 밖) 객체를 현재 세션에 병합하며, refresh와 달리 DB에서 최신 데이터를 우선 로드한 후 변경을 적용합니다
detached_user = User(id=1, name='detached_change') # 세션 밖 객체
merged = session.merge(detached_user) # DB 최신 로드 후 병합
merged.name # DB 값 + 'detached_change'
session.flush() # DB 반영
그래서 read session으로 불러와서 update SQL을 부르고 merge + refresh를 하면?
select이 2번 나가요! 아주 비효율적이기 때문에, 애초에 Write Session으로 객체를 불러오도록 하거나, 객체의 setter로 직접 변경하는 것이(트레이드 오프는 있지만) 성능에는 좋아보입니다.