<영속성 컨텍스트>
JPA에서 가장 중요한 2가지
1. 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)
2. 영속성 컨텍스트
EntityManagerFactory -> 고객요청시 EM 생성 -> EM은 내부적으로 DB connection을 사용해서 DB 자원에 접근하고 사용\
그럼 영속성 컨텍스트라는게 대체 뭔가?
한마디로 정의하면 "엔티티를 영구 저장하는 환경"이라는 뜻
-> EntityManager.persist(entity);
여기서 persist() 는 entity를 db에 저장하는게 아니다.
영속성 컨텍스트를 통해서 엔티티를 영속화 한다는뜻이다. 쉽게말해 엔티티를 영속성 컨텍스트라는곳에 저장하는것이다.
영속성 컨텍스트가 그래서 뭔데...
사실 영속성 컨텍스트는 논리적인 개념이다. 그래서 눈에 보이지않는다.
엔티티 매니저를 생성하면 안에 영속성 컨텍스트라는 눈에보이지 않는 공간이 생긴다.
<엔티티의 생명주기>
1. 비영속(new)
2. 영속(managed) .persist()
3. 준영속(detached)
4. 삭제(removed)
비영속상태는 말그대로 객체를 new 해서 생성한상태이고 entitymanager에는 영속되지 않은 상태이다. jpa와는 전혀 관계없는 상태.
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
영속상태는 EM안에 있는 영속성 컨텍스트에 new한 객체가 들어가면서 영속된 상태를 말한다.
detached는 말그대로 엔티티를 영속성 컨텍스트에서 분리시키는것을 말하고 객체를 삭제하는게 remove이다.
그럼 영속성 컨텍스트의 이점은??
1. 1차 캐시
: 영속성 컨텍스트는 내부에 1차캐시를 들고있음. 그래서 객체를 생성하고 영속하면 1차캐시 내부에 @Id / Entity로 맵핑되어 들어감.
그럼 id로 조회시 바로 DB를 조회하는게 아니라 1차캐시를 뒤져서 id에 맞는 Entity를 찾아 바로 조회한다.
만약 1차캐시에 없는 id를 조회하면 먼저 1차캐시를 조회후 DB를 조회한다. 그다음 1차캐시에 저장후 반환한다.
2. 동일성(identity)보장
:조회한 entity 객체를 ==비교시 주소가 같음
3. 트랜잭션을 지원하는 쓰기 지원
: transaction.begin();
em.persist(memberA);
em.persirst(memberB);
했을때 영속성 컨텍스트의 1차캐시에 memberA, memberB를 쌓아놓고 이떄 Insert쿼리를 만들어 쓰기지연 SQL저장소에 쌓는다.
transaction.commit(); 할때 DB에 SQL을 보낸다.
왜이런 기능을 지원하는거지?
여기서 버퍼링을 쓸수있다. .persist()할때마다 쿼리를 날리게되면 최적화할수있는 여지가 없다.
member1과 member2를 모아서 한꺼번에 보내는 버퍼링!
4. 변경감지(Dirty Checking)
: member의 속성이 member.setName("zzz")로 변경되었다. 그러고 em.persist(member)를 다시 넣지않아도
JPA는 속성의 변경을 감지해서 바뀐 이름으로 업데이트 쿼리를 날려준다.
아니 따로 업데이트 쿼리를 날리는 명령을 하지도 않았는데 어떻게 자동으로 업데이트 쿼리를 날려주는거지?
비밀은 영속 컨텍스트에 있다.
JPA는 커밋하는 시점에 내부에서 Flush가 호출됨.
커밋할때 엔티티와 스냅샷을 비교한다. 스냅샷이 뭐야?
1차 캐시에는 사실 @ID(PK)와 Entity, 그리고 스냅샷이 있다.
영속성 컨텍스트에 들어온 최초의 시점을 스냅샷을 찍어 저장해둔다. 커밋시점에 영속컨텍스트가 Entity와 스냅샷을 비교해서 달라진게 있다면
UPDATE SQL을 생성해서 쓰기지연SQL저장소에 만들어 넣는다.
위의 그림은 논리적 개념의 영속성 컨텍스트를 그림으로 만든것이다.
1차캐시에 그림과같이 @Id , Entity, 스냅샷이 있어서 엔티티와 스냅샷의 비교가 가능하다.
5. 엔티티 삭제
: Member memberA = em.find(Member.class, "memberA");
em.remove(memberA)하면 커밋시점에 Delete 쿼리가 나가게 된다.
6. 플러시(Flush)
: 영속성 컨텍스트의 변경내용을 DB에 반영하는것을 말한다.
플러시 발생시 변경감지(dirty checking)이 일어남. 1차캐시에 엔티티와 스냅샷 비교후 수정된게 있으면
수정된 엔티티를 쓰기지연 SQL 저장소에 등록한다. 그리고 쓰기지연 SQL저장소의 쿼리를 DB에 전송한다(등록, 수정, 삭제 쿼리)
그럼 영속성 컨텍스트를 플러시하는 방법은 뭐야?
em.flush() 로 직접 호출하거나 트랜잭션 커밋 또는 JPQL 쿼리를 실행하여 플러시를 자동 호출할수있다.
그럼 플러시를 하게되면 EM안에 1차캐시가 다 지워지는건가?
그건 아니다. 플러시는 쓰기지연 SQL저장소에 쌓인 쿼리들이 DB에 반영이 되는 과정이다.
7. 준영속 상태
: 말그대로 영속성 컨텍스트에서 있던 entity가 분리되는것이다. 이렇게되면 영속성 컨텍스트가 제공하는 dirty checking 같은 기능을 사용 못한다.
준영속 상태로 만다는 방법은 em.close() 영속성컨텍스를 종료/ em.detach(member) 특정(member) 엔티티만 초기화 / em.clear() 통째로 날리는
방법이있다.