[JPA]섹션3_영속성관리_내부동장방식
JPA의 내부 구조 동작 방식 (이론)
JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)
- 정적인느낌
- DB를 어떻게 설계하고 객체를 어떻게 설계해서 JPA에서 어떻게 매핑을 할 것인가?
- 영속성 컨텍스트
- 실제 JPA내부에서 어떻게 작동되나?
엔티티 매니저 팩토리와 엔티티 매니저
새로운 고객이 올때마다 엔티티매니저를 생성하여 내부적으로 데이터베이스 커넥션을 사용하여 DB를 사용하게 됩니다.
영속성 컨텍스트
- JPA를 이해하는데 가장 중요한 용어
- "엔티티를 영구 저장하는 환경" 이라는 뜻
- EntityManager.persist(entity);
- 엔티티 객체를 DB에 저장하는 개념
- 엔티티를 영속성 컨텍스트라는 곳에 저장한다는개념
엔티티 매니저? 영속성 컨텍스트?
- 영속성 컨텍스트는 논리적인 개념
- 눈에 보이지 않는다
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근
엔티티매니저를 생성하면 1:1 로영속성 컨텍스트가 생성이 됩니다.
쉽게말하여 엔티티매니저에 눈에 보이지 않는 공간에 영속성컨텍스트가 생성이 된다는 개념
엔티티의 생명주기
EntityManager.persist(entity); 통해 영속상태가 됩니다.
다음인 비영속/영속 상태에 대해서 알아봅시다.
그냥 객체를 생성한 상태
멤버객체를 생성하고 엔티티매니저에 아무것도 넣지않은 단계
JPA와 관계없이 객체만 생성한 상태
EntityManagerFactory emf= Persistence.createEntityMangerFactory(persistenceUnitName:"hello");
EntityManager em= emf.createEntityManger();
//비영속
Member member= new Member();
member.setID(100L);
member.setName("HelloJPA");
//영속
em.persist(member);
//하지만 em.persist하는 시점에 바로 DB에 쿼리가 날라가는것이 아니라
//트랜잭션을 커밋할 때에 DB에 쿼리가 날라갑니다.
//만약 커밋하기전에 em.detach(member)하면 DB에 쿼리가 날라가는것을 막습니다.
tx.commit();
영속성 컨텍스트의 이점(애플리케이션과 DB사이 중간계층에 존재)
- 1차 캐시
- 동일성(identity)보장
- 트랜잭션을 지원하기 쓰기 지연
- 변경 감지
- 지연 로딩
영속성 컨텍스트는 내부에 1차캐시라는 것을 들고 있습니다.
지금은 엔티티매니저를 영속성 컨텍스트로 이해해도 무방하다.
Member member= new Member();
member.setID(100L);
member.setName("HelloJPA");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
위의 명령어 중 findMember을 실행한다면 DB에서 조회하는 것이 아니라 1차 캐시에서 조회를 시작합니다. 찾으면 멤버 값 그대로 반환
1차캐시에는 없고 DB에는 값이 존재할 때
먼저 1차 캐시에서 조회를 하고 없다면 DB를 조회하고 그 이후에 DB의 값들을 1차 캐시에 저장하고 member2를 그대로 반환하고 다음번엔 찾을 수 있게 합니다.
엔티티 매니저는 DB 트랜잭션 단위로 만들고 트랜잭션이 끝나면 같이 종료시킨다.
고객 요청이 하나 들어와서 끝나면 같이 종료시키는 개념
위의 사진과 같이 SQL을 실행하기 전 값을 찾을 수 있음을 알 수 있습니다.
em.persist(member)에서 1차 캐시에 저장을 하기 때문에 찾을 수 있습니다.
위의 사진은 2번째의 개념입니다.
1번째를 실행하여서 ID키가 101L인 것을 DB에 insert하여 있다고 가정하고 시작합니다.
그리하여 맨 처음엔 값을 찾을 때는 1차캐시에 존재하지않아 select문을 통해 찾기시작하지만 2번째는 1차에 저장되어 select문을 하지않고 바로 찾음을 알 수 있습니다.
영속 엔티티의 동일성 보장
엔티티 등록
트랜잭션을 지원하는 쓰기 지연
굳이 왜 이렇게 하는가? 이유가 무엇인가?
em.persist를 할 때마다 한다면 최적의 여지가 없습니다.
마이바티스로 모아서 한꺼번에 하는건 어렵지만
JPA는 가능하다
엔티티 수정(변경 감지)
em.update가 필요할 거 같지만 그렇지않다
그 이유는 영속성 컨텍스트 안에 있습니다.
최초상태의 들어온 상태→스냅샷입니다.
commit을 누를때에 엔티티와 최초의 스냅샷을 다 비교하게 됩니다.
만약 비교하여 값이 변경 되었다면 Update쿼리를 생성하여 DB에 반영합니다.
플러시
영속성 컨텍스트의 변경내용을 데이터베이스에 반영
영속성 컨텍스트에 대한 변경 사항을 데이터베이스를 맞추는 작업
플러시 발생
- 변경 감지
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송( 등록 , 수정 , 삭제 쿼리)
영속성 컨텍스트를 플러시하는 방법
em.flush()←직접 사용하지는 않지만 알아는 두기
트랜잭션 커밋 - 플러시 자동호출
JPQL 쿼리 실행 - 플러시 자동호출(그렇구나 하고 넘어가기)
Memmber member = new Member(id:200L, name:"member200");
em.persist(member);
em.flush();
system.out.print("=====");
tx.commit();
flush를 해버리니 바로 DB에 반영→그이후 commit
flush를 한다고해도 1차 캐시가 지워지지는 않는다.
쓰기 지연 SQL 저장소에 있는 SQL들이 DB에 반영이 되는 과정이라고 생각.
플러시 모드 옵션
FlushModeType.Auto(커밋이나 쿼리를 실행할 때 플러시(기본값)
FlushModeType.Commit(커밋할 때만 플러시)
알고만 있어라 손대지말고 오토만 쓰는것을 권장,..
플러시는!
- 영속성 컨텍스트를 비우지 않음(이름때문에 오해)
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요→ 커밋 직전에만 동기화 하면됨
준영속상태
- 영속 → 준영속
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
- 영속성 컨텍스트가 제공하는 기능을 사용 못함
준영속 상태로 만드는 방법
1.em.detach(entity)
특정 엔티티만 준영속 상태로 전환.
Member member= em.find(Member.class, primaryKey :150L);
member.setName("AAAA")'
em.detach(member);
tx.commit();
준영속상태여서 update를 하지않는다.
2.em.clear();(1차캐시를 지우는것)
- 영속성 컨텍스트를 완전히 초기화
Member member= em.find(Member.class, primaryKey :150L);
member.setName("AAAA")'
em.clear();
Member membeer2=em.find(Member.class, primaryKey :150L);
tx.commit();
select문 두번 출력
3.em.close
- 영속성 컨텍스트를 종료