[JPA] 섹션 3 : 영속성 컨텍스트

헌치·2022년 7월 11일
0

JPA

목록 보기
2/4
post-thumbnail

노션 링크

1. 영속성 컨텍스트


0) 엔티티 매니저

//엔티티 매니저 공장 만들기, 비용이 아주 많이 든다!
EntityManagerFactory emf =     
		Persistence.createEntityManagerFactory("jpabook"); 

//공장에서 엔티티 매니저 생성, 비용이 거의 안 든다!
EntityManager em = emf.createEntityManager();

EntityManager : 엔티티의 CRUD 일 처리

  • 엔티티를 영구 저장하는 환경
    • 엔티티 매니저를 통해 영속성 컨텍스트에 엔티티를 보관/관리
  • EntityManager.persist(entity); 사용

Entity : DB와 연결되는 객체

1) 엔티티의 생명주기

1.1 비영속(new/transient)

  • JPA와 전혀 관계 없는 새로운 상태
Member member=new Member();
member.setId("member1");
member.setUsername("회원1");

1.2 영속(managed)

  • 영속성 컨텍스트가 관리하는 상태
  • em.find()JPQL로 조회된 엔티티도 영속 상태!!!
EntityManager em=emf.createEntityManager();
em.getTransaction().begin();
em.persist(member); //객체를 저장

1.3 준영속(detached)

  • 영속성 컨텍스트에서 저장 되었다가 분리된 상태
  • 해당 엔티티DB에도 저장되어 있지만, 연결되지 않음
em.detach(member);

1.4 삭제(removed)

  • 객체가 삭제된 상태
em.remove(member);

2. 영속성 컨텍스트의 이점


0) 영속성 컨텍스트와 식별자 값

  1. 영속성 컨텍스트엔티티식별자 값으로 구분
  2. 따라서 영속 상태식별자 값이 반드시 필요!!!
    1. 없을 시 예외 발생…

1) 1차 캐시 코드 조회(사실 성능 이점이 크진 않음)

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

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

//1차 캐시에서 조회 : find(클래스, 아이디) 형태
Member findMember = em.find(Member.class, "member1");

//------
// EntityManger.find() 메소드 정의
public <T> T find(Class<T> entityClass, Object primaryKey)
  • 1차 캐시 : 영속성 컨텍스트 내부의 캐시
    • 컨텍스트 내부 Map 형태로 존재
      • 키 : @Id(식별자)
      • 값 : 엔티티
  1. 조회 시 우선 1차 캐시에서 조회
  2. 1차 캐시에 없으면 DB에서 조회
  3. 조회된 값을 1차 캐시에 저장
  4. 영속 상태엔티티 반환

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

  • em.find로 가져온 두 값의 동일성을 보장!
    • 참조값이 동일
    • 1차 캐시에서 같은 엔티티를 가져옴
  • 1차 캐시
    • 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을
    • 데이터베이스가 아닌
    • 애플리케이션 차원에서 제공!

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

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

transaction.begin();  //[트랜잭션] 시작

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

//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); //[트랜잭션] 커밋
  1. transaction.begin(); : 트랜잭션 시작
  2. em.persist(memberA); : 내부 쓰기 지연 SQL 저장소에 데이터들을 저장
  3. transaction.commit(); : 커밋 시 데이터(쓰기 지연 SQL 저장소 에 모인 쿼리)를 한번에 DB에 전송

4) 변경 감지

// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//-> 커밋 시 자동으로 데이터 업데이트
  • 기존 : 수정할 필드 별로 쿼리를 작성..복잡
    • 한 쿼리로 작성 시 null 값 들어감ㅠ
    • 직/간접적으로 비즈니스 로직SQL에 의존ㅠ
  • 변경 감지 : 엔티티의 변경사항을 DB자동 반영
    • 엔티티스냅샷 비교
    • 커밋 시 데이터에 변경이 일어났으면 업데이트

3. 플러시(동기화)


  • 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화
  • 영속성 컨텍스트를 비우지는 않음!!!
  • 트랜잭션 단위로, 커밋 직전에만 동기화하면 됨!

1) 플러시 발생

  1. 변경 감지
  2. 수정된 엔티티
    1. 정확히는
      1. 영속 상태 엔티티가 수정된 후,
      2. 쓰기지연 SQL 저장소로 보내진 수정 쿼리
    2. 를 바로 DB에 저장
  3. DB 트랜잭션 커밋

1.5) UPDATE SQL

업데이트 SQL 문은 실제로, 엔티티모든 필드를 수정에 반영한다!

  • 수정 쿼리가 항상 같아 재사용 용이!
    • 어플리케이션 로딩 시점에 수정 쿼리를 미리 생성 후 재사용!
    • DB에 동일 쿼리 전달 → DB는 이전에 파싱된 쿼리를 재사용!
  • 만일 수정 데이터만 사용한 동적 UPDATE SQL 원할 시(칼럼 30개 이상)
    • 하이버네이트 확장 기능 사용
    • @org.hibernate.annotations.DynamicUpdate

2) 플러시 호출 방법

  • em.flush() : 직접 호출(자주 안씀)
  • 트랜잭션 커밋 시 자동 호출
  • JPQL 쿼리 실행 시 자동 호출
    • find() 메소드 호출 시엔 플러시 실행 안됨!

3) 플러시 모드

  • FlushModeType.AUTO: 커밋이나 쿼리 실행 시 플러시(기본 값)
  • FlushModeType.COMMIT: 커밋 시 플러시
  • 가급적 기본 설정 쓰자

4. 준영속


  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리됨
  • 이제 영속성 컨텍스트 기능 사용 X!
  • 기존 해당 엔티티 관련된 모든 SQL 저장소 쿼리들도 제거됨!

1) 준영속 상태 만드는 법

  • em.detach(entity) : 특정 엔티티준영속 상태로 전환
  • em.clear() : 영속성 컨텍스트를 완전히 초기화
    • 해당 컨텍스트모든 엔티티준영속 상태로 전환
  • em.close() : 영속성 컨텍스트종료
    • 보통 해당 방식으로 준영속 전환됨

2) 준영속 엔티티 특징

  • 거의 비영속 상태
  • 식별자 값이 들어가 있다
  • 지연로딩 불가

5. 병합


  • newEntity=em.merge(entity)
⚙️ 궁금증
entity=em.merge(entity)
entity = new Entity("헌치")
em.persist(entity)

두 방식의 차이점은??


  • 1번은 조회 후 해당 식별자가 없을 시 생성 → 편리하지만 쿼리 2번 날아감
  • 2번은 그냥 생성 → 업데이트 안되지만 쿼리 1번 날아감
  • Spring Data JPA에서는 두 기능이 통합된 save(entity) 메소드 사용
    • save메서드는 merge인지, persist인지 관계없이 return 값으로 반드시 영속성이 보장되는 인스턴스를 반환하도록 구현

관련링크

  • 준영속 엔티티에 대한 영속 상태 엔티티를 반환
  • 파라미터로 넘어온 엔티티식별자 값으로 영속성 컨텍스트를 조회
  • 찾는 엔티티가 없으면 DB에서 조회

1) 특징

  • 해당 엔티티저장하거나, 업데이트 하거나!
  • 기존 준영속 엔티티 는 여전히 준영속 상태로 남아있다
    • 엔티티는 서로 다른 인스턴스!

6. 삭제


  • em.remove(entity) : 해당 엔티티를 삭제
    • 호출 직후 : 엔티티영속성 컨텍스트에서 제거
    • 플러시 : SQL 저장소삭제 쿼리 호출!

참고자료

  • [책, 강의] 자바 ORM 표준 JPA 프로그래밍(김영한)

[JPA] save메서드로 살펴보는 persist와 merge 개념

Hibernate JPA EntityManager 핵심 기능 정리

profile
🌱 함께 자라는 중입니다 🚀 rerub0831@gmail.com

0개의 댓글