JPA를 이해하는데 가장 중요한 용어로서 엔티티를 영구 저장하는 환경이다.
영속성 컨텍스트는 논리적인 개념이며, EntityManagerFactory
를 통해 생성한 EntityManager
를 통해 접근이 가능하다.
스프링 프레임워크 같은 컨테이너 환경에서 EntityManager
는 영속성 컨텍스트와 1:N 으로 매칭된다.
비영속 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
영속 (managed) : 영속성 컨텍스트에 관리되는 상태
준영속 (detached) :영속성 컨텍스트에 저장되었다가 분리된 상태
삭제 (removed) : 삭제된 상태
예제를 들어가기 전에 JPA에서 제공하는 CRUD API에 대해 간략히 알아보자
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId(1L);
member.setName("User1");
현재 객체를 만들기만 하고 영속성 컨테이너에 올리지 않아 존재만하는 상태이다.
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername(“회원1”);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
EntityManager.persist()
메소드를 통해 영속성 컨테이너에 올릴 수 있다.
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
//객체를 삭제한 상태(삭제)
em.remove(member);
엔티티를 영속성 컨테이너에 영속 시키게 되면 이 정보는 1차 캐시에 저장되어 이후에 동일한 정보를 요청시 DB 연산을 거치지 않고 1차 캐시에서 뽑아서 준다.
insert 문 후, select문이 호출되지 않은 모습. 1차 캐시에서 바로 꺼내 왔기 때문.
클래스를 재실행 시키면 DB에 저장된 정보는 남지만, 영속성 컨테이너는 EntityManager
와 매칭되어 있기 때문에, 재실행시 EntityManager
가 새로 만들어지며 1차 캐시도 갱신된다.
기존에 저장해두었던 Id = 1L
의 데이터를 두번 불러서 같은 객체인지 확인해보았다.
결과는 select문이 실행되고 그 데이터는 1차 캐시에 저장되어 select문이 한번만 실행되었고, 같은 객체임을 알 수 있다.
데이터베이스를 공부하며 트랜잭션은 commit
을 기준으로 연산을 저장하며 오류 시 rollback
한다는 점을 이미 알고있다.
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
주석처럼 .persist()
를 통해 영속성 컨테이너에 올려도 이는 DB에 저장됬다는 의미는 아니다.
컨테이너 내의 쓰기 지연 저장소
에 저장되어 commit
이나 기타 상황을 기다린다.
JPA CRUD API중 update문에 해당하는 메소드가 존재하지 않는다.
물론 아래 더 있긴한데.. 편집 실력 부족...
그렇다면 JPA에서 어떻게 update를 하게 될까?
기존에 저장되있던 Member(1L, User1)
의 데이터를 찾아와
.setName("Change Name")
으로 이름만 바꿔주고 아무런 작업을 하지 않았으나, 콘솔에 update문이 실행되었다.
웹 콘솔에서 본 DB에도 반영된 모습.
이것이 어떻게 가능할까?
영속성 컨텍스트가 최초로 DB에서 데이터를 불러오는 시점을 스냅샷으로 저장한다.
이후 commit
이 일어날 때 flush
가 일어나는데 이 때, 스냅샷의 정보와 비교하여 변경점을 자동으로 적용시켜 준다.
flush
가 어떤 놈일까?
flush
를 간단히 말하자면 변경 사항을 감지하는 놈이다.
flush
가 호출되면 영속성 컨텍스트의 쓰기 지연 저장소에 저장된 내용을 DB에 반영시킨다. (CRUD 메소드들이 쌓여있다가 반영됨) 영속성 컨텍스트가 비워지진 않는다.
쉽게 얘기해서 영속성 컨텍스트의 변경 사항들과 DB의 상태를 맞추는 작업이다.
flush()
를 자동으로 호출할수도 있지만 수동으로 해줘야 할 때가 있다.
예를들면.. 저장하고자하는 정보를 commit
전에 불러 오고자 한다던가...
외에 JPQL 쿼리나 부가 다른 경우도 추후에 더 학습한다고 한다.