본문 바로가기
카테고리 없음

JPA 내부 구조

by bloodFinger 2020. 5. 28.

영속성 컨텍스트 ? 

-JPA를 이해하는데 가장 중요한 용어

- 엔티티를 영구 저장하는 환경이라는 뜻

- EntityManager를 영속성  컨텍스트라고  생각하면 편하다.

persist(entity)

em.remove(member)

 

비영속(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);

준영속(detached) - member를 영속성 컨텍스트에서 관리 하지 않겠다 , 즉  DB에 업데이트 등 다 안된다!

em.detach(member);

또는 em.clear();

또는 em.close();

 

삭제(removed)

em.remove(member);

 

 

영속성 컨텍스트의 왜 존재하는데?

  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

하나씩 설명하겠습니다!!!

 

 

1차 캐시

1차 캐시에 key와 value의 형식으로 객체가 저장이 된다!!

key : @id  ,  value : Entity

 

Member member = new  Member();
member.setId("member1");
member.setUsername("회원1");

//1차 캐시에 저장 
em.persist(member);

//1차 캐시에서 조회
Member findMember = em.find(Member.class , "member1");

우선적으로 1차 캐시에 접근해서  데이터를 가져올 수 있다.

그런데 예를 들어  "member2"를 find 하게 된다면 1차 캐시에 없기에 DB 조회를 하고 조회한 데이터를 1차 캐시에 저장한다.

그리고 캐시에 저장된 "member2"를 가져온다.

 

 

동일성 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b) //동일성 비교 True    

어려운 말로는

-> 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

 

결국 다시 말해서 결국엔 내부에 1차 캐시라는 개념이 존재하기  때문이다.

 

 

 

트랜잭션을 지원하는 쓰기 지연

 

옵션을  잘 설정하면 쿼리가 한번만 나간다. 그게 왜 가능한지 살펴보자  버퍼기능

 

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); //[트랜잭션] 시작

em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.

//커밋하는 순간 데이터베이스 INSERT SQL을 보낸다.
transaction.commit();

em.persist(memberA);

em.persist(memberB);

memberA는 1차캐시에 저장이 되면 서 쓰기 지연 SQL 저장소에 담겨진다.

memberB 또한 마찬가지로 1차캐시에 저장이 되면서  지연 SQL 저장소에 담겨진다.

아직 DB에 저장되지 않은 상태이다!!!

 

trasaction.commit();

커밋을 하는 순간 쓰기 지연 SQL 저장소에 있던 SQL문들이 DB로 넘어간다. 이러한 과정을 flush라고 한다.

 

 

 

엔티티 수정

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();

 

이렇게 하면 자동으로 카밋 된다.

 

Dirty Checking

영속성 컨텍스트에 1차 캐시로 저장한 Entity와 스냅샷을  비교해서 UPDATE SQL을 생성해서 DB에 날려준다.

 

 

예를 들어서 자바 컬랙션 List에 member라는 객체가 있을때 member의 값을 변경하면 알아서 List의 값이 

변경되어있습니다.

우리가 변경된 내용을 다시 저장해줄필요가 없죠.

자바 컬렉션을 다루듯이 JPA를 다루면 됩니다.

 

 

엔티티 삭제

 

 

 

 

영속성 컨텍스트를 플러시하는 방법

  • em.flush() - 직접 호출
  • 트랜잭션 커밋 - 플러시 자동 호출
  • JPQL 쿼리 실행 - 플러시 자동 호출

 

JPQL 실행시 자동으로 Commit이 된다!

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

//중간에 JPQL 실행
query =  em.createQuery("select m from Member m" , Member.class);
List<Member> members = query.getResultList();