JPA를 처음 배우면 이런 코드를 자주 보게 된다.
@Transactional
public void updateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow();
user.setName("Kim");
}
신기한 점은 save()를 호출하지 않았는데도 데이터베이스의 값이 변경된다는 것이다.
처음에는 "JPA가 원래 그런가 보다" 하고 넘어갈 수 있지만, 사실 이 현상을 이해하지 못하면 JPA를 제대로 사용하기 어렵다.
이 동작의 중심에는 영속성 컨텍스트(Persistence Context) 가 있다.
이번 글에서는 영속성 컨텍스트가 무엇인지, 왜 존재하는지, 그리고 JPA가 어떻게 엔티티를 관리하는지 자세히 알아보자.
영속성 컨텍스트란 JPA가 엔터티를 관리하는 공간이다. JPA는 엔티티(객체)를 DB에 저장하기 이전에 꼭 이 영속성 컨텍스트라는 곳에 먼저 저장을 해야한다.
애플리케이션 ⇒ 영속성 컨텍스트 ⇒ Database
중간에 존재하는 캐시 공간같은 느낌이다.

1. 성능 향상
같은 데이터를 여러 번 조회할 때 매번 DB에 접근하면 비효율적이다.
영속성 컨텍스트는 1차 캐시 역할을 수행한다.
2. 변경 감지
객체 값이 바뀌었는지 추적할 수 있다.
3. 객체 동일성 보장
같은 데이터를 조회하면 항상 같은 객체를 반환한다.
JPA 엔터티는 4가지 상태를 가진다.
1. 비영속(Transient)
객체만 생성된 상태
User user = new User();
JPA는 해당 객체를 아직 모른다.
2. 영속(Persistent)
영속성 컨텍스트가 관리하는 상태
entityManager.persist(user);
JPA가 해당 객체를 관리하기 시작한다.
해당 상태에서 Dirty Checking, 1차 캐시 등이 동작한다.
3. 준영속(Detached)
관리 대상에서 제외
entityManager.detach(user);
이후 값을 변경해도 DB에 반영되지 않는다.
4. 삭제(Removed)
삭제 예정 상태
entityManager.remove(user);
트랜잭션이 종료되면 DELETE SQL이 실행된다.
1차 캐시
내부에 엔터티를 저장할 수 있는 캐시들을 가지고 있다.
User user1 = em.find(User.class, 1L);
User user2 = em.find(User.class, 1L);

두 번째 조회는 DB에 접근하지 않는다.

DB조회 ⇒ 영속성 컨텍스트 저장 ⇒ 재사용
DB 접근을 줄여 성능을 향상시킨다.
동일성 보장
영속성 컨택스트는 같은 엔티티를 조회하면 같은 객체를 반환한다.
User user1 = em.find(User.class, 1L);
User user2 = em.find(User.class, 1L);
System.out.println(user1 == user2)
결과
true
메모리에 하나만 존재하기 때문에 true이다.
Dirty Checking (변경 감지)
@Transactional
public void updateUser(Long id) {
User user = repository.findById(id).orElseThrow();
user.setName("Kim");
}
save() 없음
update() 쿼리 없음
그래도 DB 값이 변경된다.

name = Park
이후
user.setName(Kim);
실행
name = Kim
트랜잭션 종료 시
Park ≠ Kim
확인
UPDATE user
SET name='Kim'
Flush
영속성 컨텍스트의 변경 내용을 DB에 반영
em.flush();
의 의미는
영속성 컨텍스트
∇
DB로 SQL 전송
※ 주의
flush ≠ commit
DB에 SQL만 보내고 트랜잭션은 유지된다.