JPA를 이해하는 데 가장 중요한 용어
EntityManager.persist(entity);
영속성 컨텍스트는 논리적인 개념 (눈에 보이지 않는다.)
엔티티 매니저를 통해서 영속성 컨텍스트에 접근
비영속 (new/transient)
: 객체를 생성한 최초의 상태. 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태.
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
영속 (managed)
: 영속성 컨텍스트에 관리되는 상태
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername(“회원1”);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
엔티티 매니저 안의 영속성 컨텍스트에 멤버 객체가 들어가면서 영속 상태가 된다.
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
//객체를 삭제한 상태(삭제)
em.remove(member);
- 1차 캐시
- 동일성 (identity) 보장
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
- 변경 감지 (Dirty Checking)
- 지연 로딩 (Lazy Loading)
영속성 컨텍스트는 내부에 1차 캐시를 들고있다.
1차 캐시의 key는 DB pk로 맵핑한 id이다. value(값)은 엔티티 객체 자체이다.
1차 캐시에 엔티티가 있는 경우
em.find(Member.class, "member1"); 로 조회를 하면 JPA는 영속성 컨텍스트에서 1차 캐시에서 member1 를 조회한다. 1차 캐시에 member1 엔티티가 있다면 DB에서 조회하지 않고, 1차 캐시에 있는 값을 그대로 조회해온다.
1차 캐시에 엔티티가 없는 경우
member2 객체가 1차 캐시에 없으면, DB를 조회한 후 DB에 있는 member2를 1차 캐시에 저장한다. 그리고 member2를 반환한다. 이후에 member2를 다시 조회하게 되면 영속성 컨텍스트 안의 1차 캐시에 있는 member2가 반환된다.
📍 고객 요청이 들어오고 해당 요청의 처리가 끝나면 해당 영속성 컨텍스트 및 1차 캐시는 없어진다. 즉 1차 캐시는 여러 고객이 공유해서 사용하는 캐시가 아니다. 한 트랜잭션 안에서만 효과가 있다. (애플리케이션 전체에서 공유하는 캐시는 JPA나 Hibernate 에서는 2차 캐시라고 한다.)
JPA가 영속 엔티티의 동일성을 보장해준다.
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공
em.persist(memberA); -> memberA가 1차 캐시에 들어간다. JPA가 이 엔티티를 분석해서 INSERT 쿼리를 생성해 쓰기 지연 SQL 저장소에 해당 쿼리를 쌓아둔다.
em.persist(memberB); -> 마찬가지이다.
transaction.commit();
쓰기 지연 SQL 저장소에 있는 쿼리들은, 트랜잭션을 커밋하는 시점에, flush가 되면서 DB에 날아간다. 그리고 실제 Database 트랜잭션이 commit 된다.
JPA는 Dirty Checking (변경 감지 기능)을 제공하여 엔티티의 데이터를 변경할 수 있도록 한다.
🤔 how? - 변경 감지 메커니즘
JPA에서는 트랜잭션을 커밋하면 1. flush가 발생한다.
2. JPA가 엔티티와 스냅샷을 비교한다. (스냅샷은 값을 읽어온 시점, 즉 영속성 컨텍스트의 1차 캐시에 들어온 최초 시점의 상태를 저장해둔 것이다.)
3. 엔티티와 스냅샷을 비교했을 때 바뀐 데이터들에 대해 UPDATE 쿼리를 만들어 쓰기 지연 SQL 저장소에 저장해둔다.
4. UPDATE 쿼리를 데이터베이스에 반영한다.
5. commit 한다.
변경 감지 메커니즘과 똑같이 작동한다. (DELETE 쿼리가 나감.)
플러시란, 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것이다.
데이터베이스 트랜잭션이 커밋되면 자동으로 플러시가 발생한다.
✅ 플러시가 발생하면?
변경 감지
수정된 엔티티 쓰기 지연 SQL 저장소에 등록된다.
쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다. (등록, 수정, 삭제 쿼리)
em.flush() - 직접 호출
트랜잭션 커밋 - 플러시 자동 호출
JPQL 쿼리 실행 - 플러시 자동 호출
(참고 - 플러시 모드 옵션)
💖 플러시는 !
영속 -> 준영속
영속 상태의 엔티티가 영속성 컨텍스트에서 ❗ 분리(detached)
영속성 컨텍스트가 제공하는 기능을 사용 못함
• em.detach(entity)
특정 엔티티만 준영속 상태로 전환
• em.clear()
영속성 컨텍스트를 완전히 초기화
• em.close()
영속성 컨텍스트를 종료