티스토리 뷰

ORM/JPA

[JPA] 1. 내부동작방식

mandykr 2021. 11. 6. 16:16

목차

1. 엔티티 생명주기

2. 엔티티 매니저 팩토리와 엔티티 매니저

3. 영속성 컨텍스트

4. 플러시

5. 준영속

 

 

1. 엔티티 생명주기

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

 

2. 엔티티 매니저 팩토리와 엔티티 매니저

  • 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에 서 공유
  • 엔티티 매니저는 쓰레드간에 공유X (사용하고 버려야 한다).
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabasic");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

// 트랜잭션 시작
tx.begin();
try {

	// ...

} catch (Exception e) {
	tx.rollback();
} finally {
	em.close();
}        
// 트랜잭션 종료
tx.commit();

 

3. 영속성 컨텍스트

영속성 컨텍스트 구조

1) 1차 캐시

1차 캐시를 먼제 확인해 엔티티를 가져온다. (select 쿼리가 실행되지 않음)

1차 캐시에 없는 경우 DB에서 엔티티를 가져온다. (select 쿼리 실행)

// 엔티티를 영속화
em.persist(memberA);

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

// DB에서 조회
Member memberC = em.find(Member.class, "memberC");

 

2) 동일성(identity) 보장

트랜잭션 내에서의 엔티티 동일성을 보장한다.

Member memberA1 = em.find(Member.class, "memberA");
Member memberA2 = em.find(Member.class, "memberA");

System.out.println(memberA1 == memberA2); //동일성 비교 true

 

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

트랜잭션 커밋 시점에 flush와 commit이 실행된다.

일정 size의 SQL을 모았다가 flush하는 방법으로 성능 최적화에 사용할 수 있다. (hibernate.jdbc.batch_size 옵션)

flush() : 쓰기지연 SQL 저장소에 저장되어 있는 memberA [insert SQL]을 DB로 전달하고 commit한다. (DB 반영)

em.persist(memberA); // 영속화

transaction.commit(); // flush() 자동 호출

 

4) 변경 감지(Dirty Checking)

최초로 엔티티를 읽어온 시점에 스냅샷을 저장한다.

flush() 시점에 스냅샷과 비교해 [update/delete SQL] 을 만들어 쓰기지연 SQL 저장소에 저장하고 DB로 전달한다.

// 엔티티 영속화
Member memberA = em.find(Member.class, "memberA"); // 스냅샷 저장
Member memberB = em.find(Member.class, "memberB");

// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

// 영속 엔티티 삭제
em.remove(memberB);

transaction.commit(); // flush() 자동 호출

 

변경 감지 vs merge

준영속 상태의 엔티티를 수정하는 방법은 두 가지가 있다.

 

(1) 변경 감지 : 엔티티를 영속화한 후 필드를 변경한다.

Item item = em.findById(param.getId());
item.setName("name"); //필드를 변경할 때는 setter 보다는 의미있는 메서드를 사용해야 함

트랜잭션 커밋 시점에 update 쿼리가 실행된다.

 

(2) merge

Item item = em.merge(param);

param의 id 를 통해 데이터베이스에서 엔티티를 조회한다.

param의 필드 값을 찾은 엔티티에 모두 적용한 후 변경된 엔티티를 영속화하고 리턴한다.

 

param에 값이 null인 필드가 있으면 데이터베이스에 null 값이 저장될 우려가 있다.

Spring Data Jpa의 save() 메소드를 호출할 때 준영속 상태의 엔티티를 대상으로 사용하면 merge()가 호출된다.

 

4) 지연 로딩(Lazy Loading)

작성 예정.

 

4. 플러시

1) 플러시 발생시 실행

  • 변경 감지(Dirty Checking)
  • SQL 전송

 

2) 플러시 호출 시점

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

 

5. 준영속

영속성 컨텍스트에서 관리하지 않게 변경됨(영속 -> 준영속)

  • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
  • em.clear() : 영속성 컨텍스트를 완전히 초기화
  • em.close() 영속성 컨텍스트를 종료

 

 

 

출처

https://www.inflearn.com/course/ORM-JPA-Basic 자바 ORM 표준 JPA 프로그래밍 - 기본편(김영한)

728x90

'ORM > JPA' 카테고리의 다른 글

[JPA] 6. 프록시와 연관관계 관리  (0) 2021.12.14
[JPA] 5. 고급 매핑  (0) 2021.12.01
[JPA] 4. 다양한 연관관계 매핑  (0) 2021.11.29
[JPA] 3. 연관관계 매핑  (0) 2021.11.09
[JPA] 2. 엔티티 매핑  (0) 2021.11.07