JPA 구동 방식 & 영속성 관리

Sewon·2021년 11월 19일
0

for JPA

목록 보기
2/2
post-thumbnail

JPA Dialect

JPA는 특정 데이터베이스에 종속되지 않음

JPA 전체적 구동 방식

JPA에서 중요한 2가지

  1. 객체와 관계형 데이터베이스 매핑(ORM)
  2. 영속성 컨테스트

EntityManagerFactory와 EntityManager


→ 각 요청마다 EntityManagerFactory가 EntityManager를 생성하여 처리함

영속성 컨테스트

→ "엔티티를 영구 저장하는 환경"이라는 뜻
→ 논리적인 개념으로 눈에 보이지는 않음
→ EntityManager를 통해서 영속성 컨테스트에 접근함

EntityManager.persist(entity);
  • J2SE 환경 (Standard edition)
  • J2EE, 스프링 프레임워크 같은 컨테이너 환경

Entity의 생명주기

→ 비영속 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
→ 영속 (managed) : 영속성 컨테스트에 관리되는 상태
→ 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
→ 삭제 (removed) : 삭제된 상태

1) 비영속

// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("test1");
member.setUsername("회원1");

2) 영속

// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("test1");
member.setUsername("회원1");

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaciton().begin();

// 객체를 저장한 상태 (영속)
em.persist(member);

3) 준영속

// 회원 객체를 영속성 컨텍스트에서 분리 (준영속 상태)
entityManager.persist(member);

4) 삭제

// 회원 객체를 삭제한 상태 (삭제)
entityManager.remove(member);

영속성 컨텍스트의 이점

  • 1차 캐시
  • 동일성(identity) 보장
  • 트랜잭션을 지원하는 쓰기 지연 (= 버퍼링, transactional write-behind)
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

1) 1차 캐시

// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("test1");
member.setUsername("회원1");

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaciton().begin();

// 객체 영속
em.persist(member);
  • 상황1) 1차 캐시에서 조회
// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("test1");
member.setUsername("회원1");

// 객체 영속 = 1차 캐시에 저장됨 
em.persist(member);

// 1차 캐시에서 조회 가능
Member findMember = em.find(Member.class, "test1");
  • 상황2) 데이터베이스에서 조회
Member findMember2 = em.find(Member.class, "test2");

2) 영속 엔티티의 동일성 보장

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

System.out.println(a == b); // 동일성 비교 true 반환

3) 엔티티 등록 (트랜잭션을 지원하는 쓰기 지연 = DB 반영 지연)

EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();

// EntityManager는 데이터 변경 시 트랜잭션을 시작해야 함
transaction.begin(); // [트랜잭션] 시작 

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

// 아직 Insert SQL을 데이터베이스에 보내지 않음

// 커밋하는 순간 데이터베이스에 Insert SQL을 보냄
transaction.commit(); // [트랜재션] 커밋 
  • 상황

    → Member 객체 생성 후 persist()를 하면 해당 객체는 영속 상태가 됨(영속성 컨텍스트에서 관리되는 상태)
    → 이때 Member 객체 생성하는 INSERT SQL 이 생성되지만, 바로 DB에 전송하지 않고, '쓰기 지연 SQL 저장소'에 저장해둠 & 해당 객체 1차 캐시에 저장됨

    → commit() 시 '쓰기 지연 SQL 저장소'에 저장되어있던 SQL이 DB에 전달됨

추가) batch_size 설정

<property name="hibernate.jdbc.batch_size" value="10"/>  

SQL을 모아두었다가(설정한 batch_size만큼) DB에 한방에 날림, Buffering 기능

4) 엔티티 수정 (변경 감지)

EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();

// EntityManager는 데이터 변경 시 트랜잭션을 시작해야 함
transaction.begin(); // [트랜잭션] 시작 

// 영속 엔티티 조회
Member memberA = em.find(Member.class, "test1");

// 영속 엔티티 데이터 수정
memberA.setUsername("me"); // 해당 코드만으로도 Update 쿼리가 생성되어 DB에 전달할 수 있음 

em.update(memberA); // 이러한 호출이 필요없음 ! ! !

transaction.commit(); // [트랜잭션] 커밋

→ memberA.setUsername("me"); 해당 코드에서 Update 쿼리가 생성되어 DB에 전달할 수 있음
→ em.update(memberA); 이러한 추가적인 호출이 필요없음

5) 엔티티 삭제

// 삭제 대상 엔티티 조회
Member memberA = em.find(Member.class, "test1");

// 엔티티 삭제
em.remove(memberA);

추가 개념

1) 플러시

→ flush를 해도 1차 캐시는 유지됨 (= 영속성 컨텍스트를 비우지 않음)
→ 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
'트랜잭션' 이라는 작업 단위가 중요! 커밋 직전에만 동기화 하면 됨

  • 플러시 발생의 의미
    ㄱ. 변경 감지
    ㄴ. 수정된 엔티티에 대해 쓰기 지연 SQL 저장소에 등록
    ㄷ. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
  • 영속성 컨텍스트를 플러시하는 방법
    em.flush - 직접 호출
    transaciton.commit - 플러시 자동 호출
    JPQL 쿼리 실행 - 플러시 자동 호출

2) 준영속 상태

→ 영속 -> 준영속 상태로 변경 가능함
→ 영속 상태의 엔티티가 영속성 컨텍스트에서 분리됨(detached)
→ 영속성 컨텍스트가 제공하는 기능을 사용 모함

  • 준영속 상태로 만드는 방법
    em.detach(entity) - 특정 엔티티만 준영속 상태로 전환
    em.clear() - 영속성 컨텍스트를 완전히 초기화
    em.close() - 영속성 컨텍스트를 종료
profile
for develop ☁️

0개의 댓글