[JPA] 영속성 컨텍스트

imcool2551·2022년 4월 4일
0

JPA

목록 보기
2/12
post-thumbnail

본 글은 인프런 김영한님의 JPA 로드맵을 기반으로 정리했습니다.

1. 영속성 컨텍스트


영속성 컨텍스트는 눈에 보이지 않는 논리적인 개념이다. 영어로 PersistenceContext 라고 하며 이름처럼 엔티티를 영구 저장하는 환경이라고 볼 수 있다.

애플리케이션에서는 EntityManager 를 통해 영속성 컨텍스트에 접근할 수 있다.

J2SE 환경에서는 엔티티 매니저와 영속성 컨텍스트가 1:1로 매핑된다. J2EE, 스프링 프레임워크와 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1 로 매핑된다.

2. 엔티티 생명주기


JPA가 관리하는 엔티티는 생명주기를 가진다.

  • 비영속(new/transient) : 영속성 컨텍스트와 관계 없는 새로운 상태

  • 영속(managed) : 영속성 컨텍스트가 관리하는 상태

  • 준영속(detached) : 영속성 컨텍스트에서 분리된 상태

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

코드를 통해 살펴보자.

2.1. 비영속 상태

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

위 처럼 엔티티를 직접 생성하는 경우 엔티티는 비영속 상태다 된다. EntityManager 를 통해 조회한 데이터가 아니기 때문에 영속성 컨텍스트가 해당 엔티티를 알 수가 없다.

2.2 영속 상태

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

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(member);

EntityManagerFactory로부터 EntityManager를 얻은 뒤 엔티티를 영속화하는 코드다. 엔티티를 영속화 할 때는 트랜잭션이 필요하다.

2.3 준영속 상태

em.detach(member);

위의 코드를 통해 영속 상태인 엔티티를 영속성 컨텍스트에서 분리할 수 있다. 이를 준영속 상태의 엔티티라고 부른다.

2.4 삭제 상태

em.remove(member);

위의 코드를 통해 엔티티를 삭제한 상태로 만들 수 있다. 실제로 DB에서 바로 삭제되는 것은 아니다. 영속성 컨텍스트가 해당 엔티티를 삭제 상태로 관리하게 된다. 트랜잭션이 커밋되고 영속성 컨텍스트가 flush 되어야 DB에서 지워진다.

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


영속성 컨텍스트가 주는 이점을 살펴보자.

3.1 1차 캐시

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

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

위처럼 엔티티를 영속화하면 영속성 컨텍스트는 엔티티를 1차 캐시에 저장한다. 영속성 컨텍스트는 영속 상태의 엔티티를 1차 캐시에 PK 기준으로 저장한다.

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

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

 // DB 조회 없이 1차 캐시에서 조회
 Member findMember = em.find(Member.class, "member1");

한 트랙잭션 내에서 1차 캐시에 저장되어 있는 엔티티는 DB 조회 없이 1차 캐시에서 조회할 수 있으므로 약간의 성능 향상이 있다.

1차 캐시에 없는 엔티티를 조회하면 DB에 쿼리가 나간다.

Member findMember2 = em.find(Member.class, "member2");

조회된 엔티티는 1차 캐시에 저장된다. 즉, 영속 상태로 관리된다.

3.2 영속 엔티티의 동일성 보장

엔티티는 1차 캐시에서 조회되기 때문에 마치 컬렉션에서 데이터를 조회하는 느낌을 준다.

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

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

데이터베이스단이 아닌 애플리케이션단에서 Repeatable-Read 트랜잭션 격리 수준을 제공한다고 볼 수 있다.

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

Transactional write-behind라고 한다. 등록, 수정, 삭제와 같은 데이터를 변경하는 SQL들을 버퍼링 하는 기능이다.

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

// JPA에서 모든 데이터 변경은 트랜잭션 내에서 수행해야 한다.
transaction.begin(); // [트랜잭션] 시작

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

// 아직 INSERT SQL을 데이터베이스에 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.

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

JPA는 영속화하려는 엔티티를 1차 캐시에 넣는다. 그리고 INSERT문을 생성해서 쓰기 지연 SQL 저장소에 저장한다. SQL을 바로 DB에 날리지 않고 지연하는 것이다.

저장소에 버퍼링된 SQL들은 트랜잭션 커밋 시점에 DB에 날라간다. 이를 flush라고 한다. 영속성 컨텍스트와 DB의 데이터를 동기화하는 것이라고 생각하면 된다.

데이터를 변경하는 SQL들을 버퍼링했다가 트랜잭션 커밋 시점에 한 꺼번에 DB로 보내면 네트워크를 적게 타므로 성능이 약간 향상된다.

3.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.persist(member) 가 있어야 하지 않을까?

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

JPA의 큰 편리함 중 하나는 엔티티를 마치 컬렉션의 객체 다루듯이 할 수 있다는 것이다. 이 장점이 가장 명확하게 드러나는 곳이 바로 변경 감지 기능이다.

위의 코드처럼 영속 상태 엔티티의 데이터를 수정하면 트랜잭션 커밋 시점에 JPA가 알맞은 UPDATE 쿼리를 생성해서 DB에 날린다.

이게 가능한 이유는 영속성 컨텍스트의 1차 캐시가 스냅샷을 보관하기 때문이다. 스냅샷은 엔티티를 조회한 시점의 데이터다. 그리고 트랜잭션 커밋시점에 스냅샷과 엔티티의 데이터를 비교한다. 이 과정을 변경 감지(Dirty Checking)이라고 부른다. 변경된 부분이 있다면 알맞은 UPDATE 쿼리가 SQL 저장소에 생성되고 DB로 플러시된다.

플러시는 영속성 컨텍스트(1차 캐시)의 데이터와 DB의 데이터를 동기화하는 과정이다. 영속성 컨텍스트를 플러시하는 방법은 다음과같다.

  • em.flush() - 직접 호출

  • 트랜잭션 커밋 - 플러시 자동 호출

  • JPQL 쿼리 실행 - 플러시 자동 호출 (JPQL 애서 자세히 다룬다)

다시 한번 말하지만 플러시는 영속성 컨텍스트를 DB와 동기화하는 것이다. 플러시는 영속성 컨텍스트를 비우지 않는다는 것을 기억하자. 영속성 컨텍스트를 비우려면 em.clear()를 호출하면 된다.

4. 마치며


영속성 컨텍스트는 JPA에서 가장 중요한 부분중 하나다. 1차 캐시, 쓰기 지연, 변경 감지 등 성능에 이점을 주고 편리한 기능을 제공한다. 이 외에도 프록시를 통한 지연로딩(lazy loading)을 제공하는데 이는 앞으로 이어질 다른글에서 자세히 알아보도록 하자.

profile
아임쿨

0개의 댓글