영속성 컨텍스트란
영속성 컨텍스트는 단어 그대로 엔티티(Entity)를 영속적으로 저장하는 환경이라는 뜻입니다. 하지만 JPA에서는 엔티티의 생명주기를 관리하고, 애플리케이션과 데이터베이스 사이에서 수많은 최적화 작업을 해주는 논리적인 공간(메모리 공간)이라고 이해해야 합니다.
마치 효율적인 작업을 위해 필요한 도구와 재료들을 미리 꺼내놓는 '작업대'와 같습니다. JPA는 이 작업대 위에서 엔티티들을 관리하다가, 작업이 끝나면(트랜잭션이 끝나면) 최종 결과물만 DB에 반영합니다.
💡 트렌젝션: 여러 작업을 하나의 단위로 묶어서 모두 성공하거나 모두 실팰하게 하는 것.
1. 1차캐시와 동일성(Identity) 보장
== 비교 시 true)임이 보장됩니다.// 같은 트랜잭션 내에서
// Member를 저장
Member savedMember = memberRepository.save(newMember);
// ...
// 위에서 저장한 Member의 id로 Member를 조회
Member member = memberRepository.findById(dto.getId()).orElseThrow(
() -> new IllegalStateExcetion("존재하지 않는 멤버입니다.")
)
// 여기서 savedMember는 영속성 컨텍스트에 저장되고,
// 그 이후에 동일한 엔티티를 조회하면 DB를 조회하지 않고 캐시의 엔티티를 반환합니
2. 변경 감지(Dirty Checking)
UPDATE SQL을 생성하여 DB에 반영합니다.@Transactional
public void updateMemberName(Long id, String newName) {
Member member = memberRepository.findById(id).orElseThrow(
() -> new IllegalStateExcetion("존재하지 않는 유저 입니다.")
); // 영속성 컨텍스트가 관리 시작
// member.updateName()만 호출했을 뿐이고, update 쿼리는 없는 상황!
member.updateName(newName);
} // 👈🏻 트랜잭션이 끝나는 이 시점에 JPA가 변경을 감지하고 UPDATE 쿼리를 날려준
다.
3. 쓰기 지연(TransactionalWrite-Behind)
memberRepository.save() 호출되면, 해당 엔티티는 1차 캐시에 저장되고, JPA는 이 엔티티를 DB에 반영하기 위한 INSERT SQL을 생성하여 차곡차곡 모아둡니다.UPDATE 나 DELETE 쿼리도 마찬가지로 이 저장소에 쌓입니다.📌 Flush란
Flush란 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 것을 말합니다. Flush는 영속성 컨텍스트의 '쓰기 지연 SQL 저장소'에 쌓인 SQL들을 데이터베이스에 전송하여, 컨텍스트의 상태와 DB의 상태를 맞추는 작업이고, 트랜잭션 커밋과는 다릅니다!
Flush는 DB에 SQL을 보내는 것일 뿐, 트랜잭션을 확정(Commit)하는 것은 아닙니다. 즉, 원래대로라면 쓰기 지연으로 인해 모아두었던 SQL을 트랜잭션이 끝나는 시점에 한꺼번에 DB로 보내지만, Flush를 사용하면 그 시점에 바로 SQL을 DB로 보내게 됩니다.
⚠️ 단, Flush를 한다고 하더라도 여전히 트랜잭션으로 관리가 되고 있는 상태이기 때문에 트랜잭션 롤백 시, Flush한 데이터도 롤백됩니다.