본문 바로가기
Back/JPA

[JPA] 영속성 컨텍스트

by 은z 2021. 12. 15.

본 포스팅은 김영한 강사의 자바 ORM 표준 JPA 프로그래밍 '기본편' 강의를 수강하며 정리한 내용임을 밝힌다.

DB 테스트 환경은 H2 데이터베이스, IDE는 인텔리제이(community version)를 사용했다.

 


JPA 매커니즘을 이해하고 학습하는데 가장 중요한 것이 영속성 컨텍스트를 이해하는 것이라고 한다.

이 개념과 생명주기를 이해하지 못하면 JPA를 제대로 활용할 수 없다고 한다.

 

1. 영속성 컨텍스트란?

- 엔티티를 영구 저장하는 환경(논리적인 개념이며 눈에 보이지 않음)

- EntityManager를 만들 때, 눈에 보이지는 않지만 EntityManager는 영속성 컨텍스트라는 공간을 가지게 된다.

- 자주 사용하는 EntityManager.persist(entity); 를 하면 DB에 저장하는 것 처럼 보이지만

사실은! 영속성 컨텍스트에 저장하는 것이다.

 

2. 생명주기

1) 비영속(new) : 영속성 컨텍스트와 전혀 관계없는 새로운 상태

Book book = new Book();
book.setName("JPA");
book.setAuthor("kim");

2) 영속(managed) 

- em.persist(); 하면 이 상태가 된다.

- 이때 DB에 접근하는 것이 아니라, 영속성 컨텍스트가 관리하는 상태가 된다.

Book book = new Book();
book.setName("JPA");
book.setAuthor("kim");

em.persist(book);

3) 준영속(detached) : persist(); 된 것을 em.detach();

4) 삭제(removed) : em.remove();

 

 

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

1) 엔티티 조회, 1차 캐시

위 그림은 영속성 컨텍스트를 이미지화 한 것이다.

em.persist()하면 영속성 컨텍스트의 1차캐시에 저장을 한다.

하나씩 설명을 하면 @Id는 DB PK로 매핑한 것이고, Entity에는 객체 자체가 들어가게 된다.

 

 조회를 하면, JPA는 바로 DB를 조회하는 게 아니라 먼저 1차캐시부터 가서 찾는다.

1차캐시에 member1이 있으면 DB까지 가지 않고, 1차캐시에서 가져오게 된다.

정리하자면,

1차 캐시에 이미 있으면 1차캐시까지만 가고,

없으면 DB 조회하고 1차 캐시에 저장하고 반환하게 된다.

 

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

마치 자바 컬렉션처럼 == 비교를 했을 때 true를 리턴한다.

이것 또한 1차 캐시가 있기때문에 가능한 것.

 

 

3) 쓰기 지연 지원

em.persist(memberA);
em.persist(memberB);
//여기까지 insert sql을 DB에 보내지 않는다.

//커밋하는 순간 DB에 insert sql을 보낸다.
transaction.commit();

영속성 컨텍스트 안에는 1차캐시 공간 뿐만아니라 쓰기지연 SQL 저장소도 존재한다.

memberA가 1차캐시로 들어가고 JPA가 memberA를 분석해서 쓰기지연 저장소에 쌓아둔다

그다음 memberB도 persist()하면 1차캐시로 넣고 쓰기지연 저장소에 차곡차곡 쌓는다.

 

그러면 언제 DB로 날아가는걸까?

바로 commit() 하는 시점에 쓰기 지연 저장소에 있는 애들이 flush() 날아가면서 DB에 보내진다.

 

JPA가 쓰기지연을 지원함으로 써의 이점은 매번 DB에 보내는게 아니라

버퍼링을 모아서 보낼 수 있기때문에 잘만 활용하면 성능면에서 좋다고 한다.

 

4) 변경 감지

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

memberA.setUsername("hi")

// em.persist(memberA); 이게 있어야 하지 않을까?

 엔티티를 수정할 때, 우리가 생각할 때는 set을 하고 JPA한테 update해달라고 알려줘야 할 거 같은데

JPA 영속성 컨텍스트의 내부동작때문에 변경을 감지해서 알아서 UPDATE SQL을 생성해준다.

 

commit()되는 시점에 내부적으로 flush()가 호출되면서

1차캐시와 스냅샷을 비교해서 뭔가 바뀌었으면 Update 쿼리를 쓰기지연 저장소에 쌓아둔다.

 

댓글