[명사] 영원히 계속되는 성질이나 능력.
- 국립국어원 표준국어대사전
영속성 컨텍스트 Persistence Context
Entity 객체를 영속성 상태로 관리하는 공간.
자바에서 작성된 Obejct를 DB Table에 저장하기 위해 사용한다.
JPA를 공부할 때 만나는 '영속'이라는 말이 있다.
영속 상태로 둔다, 영속을 시킨다, 영속성 컨텍스트에 저장한다 … 등등
'영속성'이라는 말은 영원히 계속되는 성질이나 능력이라는 의미를 가지고 있다.
말 그대로 DB와 동기화하여 오래 지속 되도록 하는 것이다.
✔️EntityManager
EntityManager를 통해서 Persistence Context에 접근할 수 있다.
EntityManager를 사용하는 방법에 대한 설명 글이 아니니 사용하는 방법은 다음 글에서 설명하기로 한다.
이 글에서 다룰 내용을 EntityManager
의 persist()
, flush()
, commit()
에 대한 내용이다.
❓ persist()와 flush(), commit()을 할 때 어느 시점에 DB에 직접 저장이 될까?
persist()
만으로도 내가 실행한 내용을 조회할 수 있으니 persist()로 DB에 저장이 된다.- persist()로 조회할 수 있는 건
flush()
가 자동으로 호출되기 때문이니 flush()로 DB에 저장이 된다. commit()
을 해야만 최종적으로 DB에 저장이 되고 컨텍스트를 비운다.
먼저 위 세 주장에 대한 대답을 하나로 모아서 말하면, 셋 다 맞다.
어떤 관점에서 보느냐에 따라서 셋 다 정답이 될 수 있다.
동작 구조
1. persist()
인자로 Entity를 사용해 객체를 영속하기 위해 persist()
를 사용한다.
객체를 영속한다 → 객체를 저장하기 위해 사용하는 것이다.
persist()
로 객체를 저장하면 1차 캐시 저장소
에 저장된다.
1차 캐시 저장소에 들어갔다고 해서 DB에 저장된건 아니다. 컨텍스트 안에 있는 캐시 저장소일 뿐이다.
저장해야 하는 데이터가 1차 캐시 저장소
에 들어가면 그에 맞는 SQL쿼리도 동작해야 한다.
알맞는 쿼리가 생성되어 쓰기 지연 저장소
에 저장된다.
1차 캐시 저장소: Entity Object
쓰기 지연 저장소: SQL query
2. flush()
persist()
는 단순히 위 과정을 진행한다.
DB에 값을 저장하기 위한 준비 단계를 persist()
에서 해주는 것이다.
1차 캐시 저장소와 쓰기 지연 저장소 모두 영속성 컨텍스트 안에 있다.
컨텍스트와 DB의 동기화 과정이 필요하다. 우리가 작성한 이 내용을 DB에서도 반영이 되어야 한다.
그 과정을 flush()
에서 도와준다.
flush()
를 하면 영속성 컨텍스트의 변경 내용을 DB에 반영(동기화)한다.
그리고 SELECT
를 실행하면 변경 내용이 우리 눈에 보인다.
아직 Context는 비워지지 않았지만 그래도 우리는 변경 내용을 확인할 수 있다.
만약 옆에서 같이 작업하는 동료가 DB를 조회할 경우 어떻게 될까?
다른 사용자가 TABLE을 조회해도 우리가 반영한 변경 결과는 나오지 않는다.
3. commit()
commit()
을 해야 진짜로 DB에 저장된다.
내 Context뿐만 아니라 다른 사람도 그 반영 결과를 볼 수 있다.
flush()
를 하는 건 DB에 바로 저장하는 것이 아니다.
그저 동기화를 시키기 위한 과정으로 우리 Context에서만 확인할 수 있고, 외부에서는 그 내용을 확인할 수 없다.
진짜로 외부에서도 쿼리 결과를 반환하고 싶다면 commit()
작업이 필요하다.
commit()
을 하면 쿼리 내용이 DB에 실제로 반영이 되고 되돌릴 수 없다. 말 그대로 '영속'이 되는 것이다.
4. rollback()
작업을 실행하던 도중 문제가 발생했을 때, rollback()
을 통해서 그동안 실행한(트랜잭션) 내용을 뒤로 돌린다.
ACID
를 맞춰주기 위해 rollback은 중요하다. -모두 성공하거나(commit), 모두 취소한다(rollback).
원자성(Atomicity): 모두 반영되거나, 전혀 반영되지 않아야 한다.
일관성(Consistency): 데이터베이스 무결성이 유지되어야 한다.
독립성(Isolation): 다른 트랜잭션이 접근할 수 없다.
영속성(Durability): 결과는 계속 유지되어야 한다.
문제가 생겼을 경우, 일부만 저장이 되면 데이터 무결성이 지켜지지 않는다.
만약 하나의 로직에 문제가 생기면, 해당 트랜잭션을 모두 처음으로 되돌려야 한다.
그래야 그래야 데이터끼리 부딪히지 않는다.
이처럼 rollback은 문제가 생겼을 경우 처리를 위해 중요하게 사용된다.
flush()는 변경 내용을 DB에 반영해주고, commit()은 그 반영을 확정시킨다.
❓rollback()은 어느 순간에 가능할까?
✅ flush()만 하면 rollback()을 할 수 있지만, commit()을 하면 rollback()을 하지 못한다.
애초에 COMMIT은 그 상태를 확정시키는 것으로 ACID의 원자성을 다시 작성하면 아래와 같다.
모두 반영되거나, 전혀 반영되지 않아야 한다. = ALL or NOTHING
➡️ COMMIT 이거나 ROLLBACK이어야 한다.
애초에 ROLLBACK을 해야 하는 건 COMMIT을 하지 못하는 상태라는 것이고, COMMIT을 하면 ROLLBACK을 해도 되돌리지 못한다.
commit()을 하면 트랜잭션에 있는 모든 변경 내용이 DB에 반영되어 확정되고, 하나의 트랜잭션이 끝나게 된다.
하지만 flush()를 하면 rollback()을 할 수 있다.
그저 우리는 flush()를 통해 Context와 DB를 동기화해주었을 뿐이다.
완전히 확정(COMMIT)을 하지 않았다.
때문에 위에서 제시한 세 가지 상황 중 두 가지 상황은 여기에서 설명이 가능하다.
✔️persist()로 조회할 수 있는 건 flush()
가 자동으로 호출되기 때문이니 flush()로 DB에 저장이 된다.
→ flush()로 DB와 동기화 되며, 변경을 조회할 수 있으니 Context를 통해 확인하면 저장이 되는 것이 맞다.
작성자 관점, Context를 바라보며 DB에 저장이 되었다 라고 말하는 것.
✔️commit()
을 해야만 최종적으로 DB에 저장이 되고 컨텍스트를 비운다.
→ 최종적으로 DB에 확정을 시키는 과정으로, 결과가 DB에 저장되고 트랜잭션이 종료된다.
객관적으로, 외부에서도 DB에서 변경 내용을 확인할 수 있으니 DB에 저장이 되었다 라고 말하는 것
처음에 어떤 관점으로 보냐에 따라서 셋 다 맞을 수 있다고 했다.
결정적으로 '진짜 반영'이 되는 건 commit()을 해야만 반영이 된다.
그러니 알맞는 정답 하나를 고르라고 하면 세번째 주장 commit()을 해야 한다가 진짜 정답이 된다.
❓ persist()로 조회가 되는 건 어떻게 가능할까?
세 가지 주장 중에 풀리지 않은 하나가 남았다. 바로 첫번째 주장
persist()
만으로도 내가 실행한 내용을 조회할 수 있으니 persist()로 DB에 저장이 된다.
flush()
로 DB와 동기화가 가능해져 나는 그 결과를 조회할 수 있다.
commit()
은 변경 내용을 확정시키는 것으로 확실히 반영이 완료된다.
persist()
는 flush()처럼 동기화를 하지 못하고, commit()도 당연히 아니지만 flush()를 실행한 것과 같은 결과를 볼 수 있다.
✅ EntityManager.serFlushMode(FlushModeType = AUTO | COMMIT)
FlushModeType
을 이용해 flush 모드를 지정할 수 있다.
AUTO
- Default로 지정되며, commit이나 query실행 시 flush를 자동으로 실행하여 DB와 동기화한다.
COMMIT
- 모든 트랜잭션이 commit될 때까지 동기화를 지연한다.
일반적으로 따로 수정하지 않으면 flush모드는 AUTO(자동 실행)으로 설정된다.
따로 flush()를 하지 않고, 바로 commit()을 하더라도 자동으로 flush()되어 동기화를 해주고, 쿼리 내용을 저장한다.
또한 flush가 필요한 시점을 자동으로 감지해 쿼리 실행 전에 flush()를 수행한다.
단순히 데이터를 저장하고 바로 SELECT를 해도, 저장된 결과를 조회할 수 있는 것은 이 flush모드가 AUTO로 지정되어 있기 때문에 가능한 것이다.
만약 flush모드가 COMMIT일 경우에 flush()를 하지 않고, 조회하면 어떤 결과가 나올까?
아무것도 나오지 않는다. 그 어떤 변경 내용도 DB와 동기화되지 않고 원래 상태 그대로 조회된다.
❓ JPQL에도 flush()가 있을까?
DB에 쿼리를 직접 전송하는 JPQL을 사용하는 방법도 flush()를 내부적으로 호출한다.
직접 DB에 쿼리를 전달한다고 하지만, Context와 동기화를 해줘야 하는 문제는 사라지지 않는다.
서로 내용이 다를 경우, 많은 문제가 발생할 것이다.
JPQL로 직접 변경을 적용시키더라도 flush()는 자동으로 호출된다.
결론
✔️ persist()
만으로도 내가 실행한 내용을 조회할 수 있으니 persist()로 DB에 저장이 된다.
→ DB에 저장되지 않는다. AUTO모드 일경우 내부적으로 flush()을 호출해 저장이 되는 것 처럼 보인다.
✔️ persist()
로 조회할 수 있는 건 flush()가 자동으로 호출되기 때문이니 flush()로 DB에 저장이 된다.
→ flush()를 호출한다고 해도 DB에 직접 결과가 반영되는 것은 아니다. 그저 동기화 작업만 진행되며, rollback()이 될 수 있다.
✔️ commit()
을 해야만 최종적으로 DB에 저장이 되고 컨텍스트를 비운다.
→ COMMIT 상태가 DB에 변경 내용을 확실하게 반영시키는 것이다.
flush는 아직 DB에 값이 저장되지 않은 상태
라고 알고 있었는데,flush()로 DB에 반영한다
는 내용을 보고 궁금해서 찾아보았다. 사람마다 flush()가 저장이 된다고 하는 사람도 있고, 아직 아니라고 하는 사람도 있었다.
데이터를 다루는 과정은 중요하기 때문에 잘못 이해했다가 문제가 생길 것 같아 자세히 알아보기 위해 이것 저것 찾아보고 질문을 했었다. 결국 flush에서 말하는 '반영 된다', '동기화 된다'와 commit이 말하는 '반영'의 차이를 알았다.
사용하는 단어는 같지만 그 의미는 확실히 다르고, 제대로 구분하려면 반영이 아닌 동기화라는 단어를 사용해야 한다.
정리
flush는 DB와 Context를 동기화 해주는 작업이며, DB에 실제 반영은 commit을 해야만 적용된다.
reference
inflearn QnA [JPA flush와 commit의 차이가 뭔지 이해가 안갑니다.]
https://www.inflearn.com/community/questions/1236565/jpa-flush-%EC%99%80-commit-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EB%AD%94%EC%A7%80-%EC%9D%B4%ED%95%B4%EA%B0%80-%EC%95%88%EA%B0%91%EB%8B%88%EB%8B%A4?srsltid=AfmBOoqfp4GDrQEg_uJg9IGYRuDiGw8xUGSHBHCnMY2R2GdR6cEqf1DA
Baeldung [Correct Use of flush() in JPA]
https://www.baeldung.com/spring-jpa-flush
'Database > JPA' 카테고리의 다른 글
[SpringBoot/JPA] 조회 성능 개선하기 - FULLTEXT Index 사용 (0) | 2025.03.18 |
---|---|
[SpringBoot/JPA] JDBC Batch Insert를 통해 대용량 데이터 저장하기 (0) | 2025.03.18 |