1) 영속성 컨텍스트(Persistence Context)
2) 객체 & 관계형 DB 매핑
[ 설명 ]
- 엔티티를 영구 저장하는 환경
- 눈에 보이지 않는
논리적인 개념
Entity Manager
를 통해 접근// 영속 상태로 등록 em.persist(member);
Entity Manager Factory
?Entity Manager
?
[ Entity 의 생명주기 ]
- 비영속 (new / transient)
: 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태Member member = new Member(); member.setId("memberA"); member.setName("kjw");
- 영속 (managed)
: 영속성 컨텍스트에 관리되는 상태Member member = new Member(); member.setId("memberA"); member.setName("kjw"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); /* 영속성 컨텍스트에 영속화 하는 것 */ em.persist(member);
- 준영속 (detached)
: 영속성 컨텍스트에 저장되었다가 분리된 상태/* 특정 객체를 영속성 컨텍스트에서 분리하는 것 */ em.detach(member); /* 영속성 컨텍스트 전체를 비우는 것 */ em.clear(); /* 영속성 컨텍스트를 종료하는 것 */ em.close();
- 삭제 (removed)
: 삭제된 상태em.remove(member);
1) 1차 캐시
2) 동일성(identity) 보장
3) 트랜잭션을 지원하는 쓰기 지연 (transactional wirte-behind)
4) 변경 감지 (Dirty Checking)
5) 지연 로딩 (Lazy Loading) - 추후 설명!
- 영속성 컨텍스트는 자체적으로
캐시(cache)
를 가지고 있다- 영속성 컨텍스트에
영속화
되어있는 객체에 대한 조회시cache
에서 가져온다
--> 사실시간적인 이점
보다는 이러한구조
덕분에 오는다른 이점
들이 크다- 영속화 되는 2가지 경우 (cache에 등록되는 2가지 경우)
em.persist()
로 영속화- cache에 등록되어 있지 않을 때,
em.find()
하는 경우
- 하나의 Transaciton마다 하나의 영속성 컨텍스트가 생성되는데,
이 때 같은 객체를 참조하면 항상 동일한 참조 객체를 가져온다.
[ 설명 ]
em.persist(member)
은 영속화 하는 것이지, DB에 저장되는 것은 아니다- 영속화되어 관리되는 객체들은 반드시
flush
과정을 거처야 실제 DB에 반영된다
(정확히는flush
이후commit
되어야 저장됨)
- 영속화를 하면 2가지 과정이 수행된다
- 해당 내용의 쿼리가
쓰기 지연 SQL 저장소
에 저장- 해당 객체가
1차 캐시
에 저장
- 결과적으로 쓰기가 지연되었다가
flush
로 인해 한번에 반영된다
[ 동작 원리 ]
쓰기 지연 SQL 저장소
에 해당 내용의 쿼리가 저장1차 캐시
에 해당 객체가 저장(없을 경우)
tx.commit()
되면flush
과정으로 인해 쌓인 쿼리가 수행flush
이후 실제 DB에 저장하는commit
이 수행된다
[ 의문 ]
- JPA 관련 메서드 어디에도 수정에 관한 메서드는 없다?
--> 객체의 수정에 관해서는Dirty Checking
이 있기 때문!
[ 설명 ]
em.find()
를 통해 가져온 객체를 수정한 뒤em.persist()
를 해야하나?
--> 그렇지 않다!Dirty Checking
을 통해 알아서 수정 쿼리가 등록됨
Dirty Checking
은 어떻게 일어나는가 ?
1차 캐시
에는pk, Entity
말고도스냅샷
이라는 것을 저장한다스냅샷
은 해당 객체의 최초 조회상태를 기록한 상태이다- 객체에 대한 수정이 일어나면 해당 객체의
스냅샷
과 비교하고, 자동으로UPDATE
쿼리문이쓰기 지연 SQL 저장소
에 저장된다
[ 개념 ]
- 영속성 컨텍스트의 변경내용을 DB에 반영하는 것
flush
과정이 시작되면쓰기 지연 SQL 저장소
에 있는 쿼리가 수행된다
[ flush 하는 방법 ]
- 직접호출
:em.flush()
를 통해 직접 flush를 수행할 수 있음
(flush가 호출되기 때문에 쿼리가 수행된다!)
- Transaction Commit - 자동 호출
: 트랜잭션이 commit되면 자동으로 flush를 호출한다
- JPQL 쿼리 수행 - 자동 호출
: JPQL쿼리가 수행되면 기본적(default)으로 flush가 수행된다
- 옵션을 통해 지정 가능 (거의 안쓰임)
FlushModeType.AUTO
: 커밋이나 쿼리를 실행시 플러시FlushModeType.COMMIT
: 커밋할 때만 플러시em.persist(memberA); em.persist(memberB); em.persist(memberC); // 중간에 JPQL 실행 --> flush 자동 수행! // 지금까지 영속화로 저장된 모든 쿼리문이 수행되게 된다 query = em.createQuery("select m from Member m", Member.class); List<Member> members= query.getResultList();
[ 주의 ]
- 플러시(flush)는 영속성 컨텍스트를 비우는 것이 아니다!
(flush한다고1차 캐시
가 지워지는 것이 아님)- 영속성 컨텍스트의 변경 내용을 DB에
동기화
한다고 이해해야한다