JPA를 공부하다보면 영속성 컨텍스트.. 영속성 컨텍스트.. 엄청 자주 나오는 단어이다. 영속성 컨텍스트에 대해 알아보자.
영속성 컨텍스트(Persistence Context)는 엔티티를 영구 저장하는 환경 이라는 뜻이다.
EntityManager.persist(entity) 는 사실 DB에 entity를 저장한다는 뜻이 아니라, 영속성 컨텍스트에 entity를 저장한다는 뜻이다.
EntityManager 안에 영속성 컨텍스트 라는것이 있다.
영속성 컨텍스트를 이해하기 위해 엔티티의 생명 주기를 알아보자.
비영속 (new/transient)
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
영속 (managed)
영속성 컨텍스트에 관리되는 상태
준영속 (detached)
영속성 컨텍스트에 저장되었다가 분리된 상태
삭제 (removed)
삭제된 상태
코드로 확인해보자.
public class JpaMain {
public static void main(String[] args) {
// EntityManagerFactory는 데이터베이스당 1개!
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// 트랜잭션 단위인 작업에는 entityManager를 만들어줘야한다!!
// entityManager는 쓰레드간에 공유하면 안된다. 쓰고 버려야함.
EntityManager entityManager = emf.createEntityManager(); // 엔티티매니저 만들기
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야함.
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// JPQL은 객체지향 쿼리언어이다. JPQL은 테이블 대상이 아닌 엔티티 객체를 대상
try{
// 비영속 상태
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
// 영속 상태
entityManager.persist(member); // 영속성 컨텍스트에 저장된 상태! DB저장은 아님!
transaction.commit(); // Insert 쿼리가 실행되어 DB에 저장됨
} catch (Exception e){
transaction.rollback();
} finally {
// close
entityManager.close();
emf.close();
}
}
}
해당 코드에서
member 라는 엔티티를 생성만 한 상태는 비영속 -> 아직 영속성 컨텍스트에 등록X
entityManager를 통해 persist 한 상태는 영속 -> 영속성 컨텍스트에 등록O
transaction.commit 메소드를 통해 DB에 member가 저장된다.
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
entityManager.detach(member);
// 객체를 삭제한 상태(삭제) -> DB 에서 삭제된다.
entityManager.remove(member);
entityManager.detach를 하면 준영속 -> 영속성 컨텍스트에서 나온다.
entityManager.remove를 하면 삭제 -> DB에서 삭제가 된다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
최종적으로 commit시 Insert 쿼리문이 DB에 반영된다.
영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것을 의미한다.
아래 경우에 flush가 발생한다.
플러시가 발생하면 쓰기 지연 저장소에 저장된 SQL(INSERT/UPDATE/DELETE)이 데이터베이스로 전송된다.
또한, 플러시가 발생했다고 해서 영속성 컨텍스트가 비워지는 것이 아니고, 변경 사항을 DB와 동기화 하는 것을 의미한다.
즉, flush가 발생해도 1차 캐시는 유지된다.
그런데, JPQL 쿼리가 실행될 때 flush가 발생하는 이유는 무엇일까?
em.persist(memberA); // 영속성 컨텍스트에 추가
em.persist(memberB); // 영속성 컨텍스트에 추가
em.persist(memberC); // 영속성 컨텍스트에 추가
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
위의 코드에서 memberA,B,C가 영속성 컨텍스트에 추가되어 영속 상태가 되었다.
중간에 JPQL을 실행하여 Member 테이블의 모든 데이터를 조회한다. 이때, flush가 발생하지 않으면 memberA,B,C는 조회한 데이터에 포함되지 않게된다!
그래서 JPQL 수행 전 자동으로 flush를 발생시켜 DB와 영속성 컨텍스트간의 동기화 작업을 수행한다.
주의할 점은 DB에는 트랜잭션이라는 작업 단위가 존재하기 때문에 한 트랜잭션 내에서 flush가 아무리 발생해도 DB commit은 발생하지 않는다는 점이다.
DB commit은 해당 트랜잭션이 commit 될 때 발생한다.