JPA 내부 구조
JPA에서 가장 중요한 2가지 중 영속성 컨텍스트에 대해 알아보도록 리뷰 하도록 하겠습니다.
엔티티 매니저 팩토리와 엔티티 매니저
- 웹 어플리케이션 구동 시 EntityManagerFactory를 생성하여 가지고 있으며, 사용자의 요청 시 EntityManagerFactory가 사용자 각각의 요청에 따라 EntityManager를 각각 생성하여 ConnectionPool을 사용해서 DB를 핸들링 합니다.
영속성 컨텍스트란?
- 엔티티를 영구 저장하는 환경
- EntityManager.persist(entity);
- 영속성 컨텍스트는 논리적인 개념
- 눈에 보이지 않음
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근
엔티티의 생명 주기
- 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속(managed) : 영속성 컨텍스트에 관리되는 상태
- 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(removed) : 삭제된 상태
비영속
- 영속 컨텍스트에 저장되지 않고 객체만 생성한 상태
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
영속
- 영속 컨텍스트에 저장된 상태
//비영속
Member member = new Mmeber();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
준영속, 삭제
//회원 엔티티를 영속성 컨텍스에서 분리, 준영속 상태
em.detach(member);
//객체를 삭제한 상태(DB 삭제)
em.remove(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차 캐시에 없을 경우
Member finMember2 = em.find(Member.class, "member2");
1. "member2" 가 1차 캐시에 없음
2. DB에서 "member2"를 조회
3. "member2"를 1차 캐시에 저장 후 "member2"를 반환
2. 영속 엔티티의 동일성 보장
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);
me.persist(memberB);
//INSERT SQL을 데이터베이스에 보내지 않음
transaction.commit(); // 트랜잭션 커밋 순간 데이터베이스에 INSERT 진행
memberA, memberB 둘다 커밋 전에는 데이터베이스에 INSERT 되지 않습니다.(쓰기 지연 SQL 저장소에 저장)
em.persist(memberA);
em.persist(memberB);
transaction.commit();
참고
JPA 영속성 컨텍스트의 1차캐시 & 쓰기지연은 정말 동작하는가?
JPA는 엔티티를 영속성 컨텍스트에서 관리한다. 영속성 컨텍스트에 대한 장점을 다시 한번 살펴보면 다음과 같다. 1. 1차 캐시 2. 동일성 보장 3. 트랜잭션을 지원하는 쓰기 지연 4. 변경 감지 이러
jobc.tistory.com
4. 엔티티 수정 - 변경 감지
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
//영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
//영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
transaction.commit(); //트랜잭션 커밋
변경 감지(Dirty Checking)
- 1차 캐시에는 @Id, Entity, 스냅샷이 있습니다. 최초로 1차 캐시로 저장되는 순간 스냅샷을 찍어 저장합니다.
- 엔티티와 스냅샷을 비교 후 변경 된 것이 있으면 쓰기 지연 SQL 저장소에 UPDATE SQL을 저장하고 COMMIT 시 수행하게 됩니다.
5. 엔티티 삭제
//삭제 대상 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA); //엔티티 삭제
플러시
영속성 건텍스트의 변경 내용을 데이터베이스에 반영
플러시 발생
- 변경 감지
- 수장된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
영속성 컨텍스트를 플러시하는 방법
- em.flush() - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 호출 - 플러시 자동 호출
JPQL 쿼리 실행 시 플러시가 자동으로 호출되는 이유
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("selectm from Member m". Member.class);
List<Member> members = query.getResultList();
JPA의 기본으로 JPQL 쿼리 실행 시 flush()를 자동으로 날립니다.
참고: 플러시는 영속성 컨텍스트를 비우지 않는다.
준영속
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못함
준영속 상태로 만들기
1. em.detach(entity) : 인자값 준영속 상태로 전환
2. em.clear() : 영속성 컨텍스트 초기화
3. em.close() : 영속성 컨텍스트 종료
'개발 > JPA' 카테고리의 다른 글
고급 매핑 (0) | 2022.06.07 |
---|---|
다양한 연관관계 매핑 (0) | 2022.06.06 |
연관관계 매핑 기초 (0) | 2022.06.05 |
엔티티 매핑 (0) | 2022.06.03 |
JPA소개 및 시작 (0) | 2022.05.31 |