JPA에서 가장 중요한 두가지❗️
1️⃣ 객체와 관계형 데이터베이스 매핑하기
2️⃣ 영속성 컨텍스트
실제 JPA가 내부에서 어떻게 동작하는지는 영속성 컨텍스트를 통해 이해할 수 있다!
➡️ 엔티티 매니저 팩토리가 고객의 요청이 올때마다 앤티티 매니저를 생성함
비영속 (new/transient)
➡️ 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
영속 (managed)
➡️ 영속성 컨텍스트에 관리되는 상태
준영속 (detached)
➡️ 영속성 컨텍스트에 저장되었다가 분리된 상태
삭제 (removed)
➡️ 삭제된 상태
EntityManager.persist(entity);
📌 영속
//객체를 생성한 상태(비영속) Member member = new Member(); member.setId("member1"); member.setUsername(“회원1”); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); //객체를 저장한 상태(영속) em.persist(member);
1️⃣ 1차 캐시
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
➡️ 처음에는 디비에서 가져오지만, 그 이후로는 1차 캐시에서 먼저 조회를 한다!
➡️ 1차 캐시에 있다면 그 캐시에서 데이터를 가져오고 없다면 디비에서 조회!
2️⃣ 동일성(identity) 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
➡️ 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공!
3️⃣ 트랜잭션을 지원하는 쓰기 지연
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
➡️ 쿼리는 persist()
가 아닌 commit()
을 할 때 날아간다!
try {
Member member1 = new Member(150L, "A");
Member member2 = new Member(160L, "B");
em.persist(member1);
em.persist(member2);
System.out.println("================");
// 데이터를 생성했으니 트랜잭션 커밋
tx.commit();
}
⬆️ 실행 시,
commit()
을 할 때 쿼리가 날아가는 것을 확인!!
4️⃣ 변경 감지(Dirty Checking)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
// em.update(member) 이런 코드가 있어야 하지 않을까???
transaction.commit(); // [트랜잭션] 커밋
➡️ 커밋하면 플러쉬라는 것이 호출되고 엔티티와 스냅샷을 비교한다.
➡️ 1차 캐시에는 사실 스냅샷이라는 것이 존재하는데, 이는 내가 값을 최초로 읽어온 시점을 스냅샷으로 저장해둔다!
➡️ 데이터가 변경이 되는 시점에 JPA가 엔티티와 스냅샷을 비교하고, 업데이트 쿼리를 쓰기지연 SQL 저장소에 만들어둔다
➡️ 마지막으로 그것을 데이터베이스에 반영하고 커밋!
플러시란❓❓
➡️ 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것!
➡️ 영속성 컨텍스트의 쿼리들을 디비로 날려 같은 상태를 만들어 주는 것!
1️⃣ 변경 감지
2️⃣ 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
3️⃣ 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
1️⃣ em.flush()
- 직접 호출
2️⃣ 트랜잭션 커밋 - 플러시 자동 호출
3️⃣ JPQL 쿼리 실행 - 플러시 자동 호출
📌 플러시는!
- 영속성 컨텍스트를 비우지 않음
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 ➡️ 커밋 직전에만 동기화 하면 됨
나중에 더 자세히 배울 예정이니 이런게 있다~ 정도만 이해하고 넘어가자🤗
준영속 상태란❓❓
( 1차 캐시에 올라간 상태가 바로 영속 상태 = JPA가 관리하는 상태 )
➡️ 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상황!
➡️ 영속성 컨텍스트가 제공하는 기능을 사용 못함
1️⃣ em.detach(entity)
- 특정 엔티티만 준영속 상태로 전환
2️⃣ em.clear()
- 영속성 컨텍스트를 완전히 초기화
3️⃣ em.close()
- 영속성 컨텍스트를 종료
빠이티잉~~🤍