JPA는 마치 컬렉션을 수정하듯이 엔티티를 수정할 수 있게 해준다.
// 트랜잭션 시작
Car car = em.find(Car.class, 1L);
car.setBrand("kia");
// 트랜잭션 종료 (커밋)
em.find() 로 조회한 car는 영속성 컨텍스트가 관리하는 엔티티다.
위의 코드를 실행하면 무슨 일이 일어날까?
뭔가 허전해보이지만,
DB에 UPDATE 쿼리가 날아간다.
/** JPQL **/
update Car c set c.brand = 'kia' where c.id = 1;
읭? 코드는 JVM 에서만 동작할 것 같이 생겼지만,
네트워크를 타고 DB까지 쿼리가 날아간다.
무슨일이 일어났던 것일까?
이 신기한 마법을 부리는 장본인은 영속성 컨텍스트다.
영속성 컨텍스트는 엔티티를 보관할때 최초 상태를 복사해서 따로 저장해둔다.
이것을 스냅샷이라고 한다.
그리고 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
위의 코드가 실행될 때 영속성 컨텍스트가 하는 일의 과정을 보면 이렇다.
em.find() 가 호출된다. 파라미터로 받은 식별자로 1차 캐시에서 먼저 엔티티가 있는지 확인한다. 없으면 DB에서 조회한다.
조회한 엔티티 (최초 상태)를 복사해서 따로 저장해둔다.(스냅샷)
car.setBrand() 메서드가 호출되면서 엔티티가 변경됐다.
커밋 전에, EntityManager 의 flush() 메서드가 호출된다.
이때 스냅샷과 현재 엔티티를 비교한다.
차이가 있으면 수정 쿼리를 생성해 쓰기 지연 SQL 저장소에 보관한다.
쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.
데이터베이스 트랜잭션을 커밋한다.
여기서 주의해야 할 점은 변경감지 기능은 트랜잭션 안에서만 활성화된다는 것이다.
만약 트랜잭션 밖에서 엔티티를 수정한다면 아무 일도 일어나지 않는다.
주의해서 엔티티를 수정하자!