[기술 면접] 영속성 컨텍스트란?

송진영·2023년 8월 20일
0

기술면접

목록 보기
24/24

영속성 컨텍스트란?

영속성 컨텍스트란 엔티티를 영구 저장하는 환경이라는 뜻이다. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

em.persist(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) 상태: 영속성 컨텍스트에 저장되었다가 분리된 상태

// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
  • 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않는다.
  • 식별자 값을 가지고 있다.

삭제(removed) 상태: 삭제된 상태

// 객체를 삭제한 상태(삭제)
em.remove(member);
// 영속성 컨텍스트를 비워도 관리되던 엔티티는 준영속성 상태가 된다.
em.clear();
// 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태가 된다.
em.close();

영속성 컨텍스트의 이점

1. 1차 캐시

영속성 내부에는 1차 캐시가 존재한다. 영속 상태의 엔티티를 이곳에 저장하기 때문에 만약 엔티티를 조회했을 때 1차 캐시에 엔티티가 존재한다면 DB를 찾아보지 않아도 된다. 말 그대로 캐시로써의 기능과 장점을 가지고 있다.

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

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

System.out.println(a==b) // true

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(); // [트랜잭션] 커밋

em.persist()로 객체를 영속성 컨텍스트에 저장해도 DB에 바로 Insert 쿼리를 날리지 않는다.
SQL 쿼리들을 모아놓았다가 flush 될 때(영속성 컨텍스트의 변경 내용을 DB에 반영할 때) 모아둔 쿼리를 모두 날린다.
이를 쓰기 지연이라고 한다.

4. 변경 감지(Dirty Checking)

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(); // [트랜잭션] 커밋

영속성 컨텍스트에서 엔티티를 조회해서 해당 엔티티를 수정한다고 하자.
이때 조회한 엔티티를 다시 업데이트하는 코드가 있어야 할 것 같지만, 그러한 코드가 없어도 영속성 컨텍스트 내의 스냅샷과 엔티티를 비교해 변경된 엔티티가 있으면 Update 쿼리를 자동으로 생성한다.

물론 이 Update 쿼리도 쓰기 지연이 될 수 있다.

5. 지연 로딩(Lazy Loading)

지연 로딩은 연관 관계 매핑되어 있는 엔티티를 조회 시 우선 프록시 객체를 반환하고, 실제로 필요할 때 쿼리를 날려 가져오는 기능이다.

즉, 필요할 때 데이터를 가져오는 기능이다.

플러시

플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다. 영속성 컨텍스트의 엔티티를 지우는 게 아니라 변경 내용을 데이터베이스에 동기화하는 것이다.

플러시의 흐름
1. 변경 감지가 동작해서 스냅샷과 비교해서 수정된 엔티티를 찾는다.
2. 수정된 엔티티에 대해서 수정 쿼리를 만들고 SQL 저장소에 등록한다.
3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.

플러시 하는 방법

  1. em.flush()
  2. 트랜잭션 커밋시 자동 호출
  3. JPQL 쿼리 실행 시 자동 호출
profile
못하는 건 없다. 단지 그만큼 노력을 안 할 뿐이다.

0개의 댓글