[Spring] Entity, EntityManager and Persistence Context

bluewhale·2021년 5월 23일
1

Spring

목록 보기
4/9

스프링에서 JPA를 다루다보면 EntityManager 객체를 만나게 된다.EntityManagerJPA 인터페이스의 일부로, 자바 객체(@Entity)를 DB에 저장된 데이터와 맵핑해주는 ORM 기술을 정의한 인터페이스이다.

@Repository
public class JpaRepository {

    @PersistenceContext
    private EntityManager em; // 바로 요놈!
}

이번 글에서는 EntityManagerPersistence Context에 대해 자세히 알아보도록 하자.

 

EntityManager

EntityManager는 이름 그대로, @Entity 어노테이션을 달고 있는 Entity 객체들을 관리하며 실제 DB 테이블과 매핑하여 데이터를 조회/수정/저장 하는 중요한 기능을 수행한다. EntityManagerPersistenceContext라는 논리적 영역을 두어, 내부적으로 Entity의 생애주기를 관리한다.

 

Entity의 생애주기

Entity 객체는 크게 다음의 네 가지 상태 중 하나의 상태를 갖는다.

New

처음 생성되어 아직 EntityManger의 관리를 받지 않는 상태를 의미한다. 이 상태에 있는 Entity는 순수한 자바 객체라고 볼 수 있다.

Managed

EntityManager에 의해 관리되고 있는 단계이다. EntityManager에서 persist() 메서드를 호출하거나 JPQL로 쿼리된 Entity 객체는 이 상태에 놓이게 된다. managed 상태는 Entitymanager 내부의 Persistence Context에 데이터가 저장된 일종의 캐싱 상태에 가까우며, DB에 실제 데이터가 저장된 것을 의미하는 것은 아니다. 이는 EntityManager의 특징인 쓰기지연(Transactional Write-Behind)과 관련이 있다.

Detached

EntityManager가 더 이상 관리하지 않는 상태로, managed 상태에서 EntityManager로 부터 분리된 상태이다

Removed

EntityManagerPersistence Context에서 삭제된 상태이다. Detached 상태와 달리 Removed 상태가 되면, DB에 맵핑된 데이터도 함께 제거된다.

@Repository
public class JpaRepository {

    @PersistenceContext
    private EntityManager em; // 바로 요놈!
    
    public Long save(Member member) { // 1. new 상태
        em.persist(member); // 2. managed 상태
        em.detach(member); // 3. detached 상태
        return member.getId();
    }
}

EntityManager의 특징

EntityManager는 데이터베이스와 어플리케이션 사이에 위치하여, 편리한 기능들을 제공한다. 대표적인 특징으로는 First Level Cache, Identity, Write-Behind, Dirty Check, Lazy Loading 등이 있다.

First Level Cache

EntityManagerPersistence Context에 등록된 Entity 객체의 @Id 어노테이션이 붙은 필드 값을 사용하여 Map<Id, Entity>형태로 저장한다. 어플리케이션에서 데이터 조회를 요청하면, EntityManager는 먼저 내부에 캐싱된 Entity가 있는지 확인한다. 데이터가 존재하면 DB에 쿼리를 전송하지 않고, 캐싱된 Entity를 반환하기 때문에 쿼리 성능이 최적화된다.

Identity

앞서 설명한 바와 같이, EntityManager는 내부적으로 Entity를 저장한다. 따라서, 동일한 Entity를 요청할 때에는 동일한 Entity를 반환함을 보장한다.

Write-Behind

EntityManager는 실제 트랜젝션이 커밋되기 전까지 발생한 쿼리를 모아서, commit()이 호출될 때 한번에 전달한다. 이러한 방식은 데이터를 처리하는 과정에서 의도치 않은 에러가 발생할 경우 roll-back이 용이하며, 네트워크 비용을 최소화할 수 있다는 장점을 지닌다.

Dirty Check

EntityManager에 등록된 Entity에 수정이 발생하면, 트랜잭션이 커밋되는 시점에 First Level Cache와의 비교를 통해 변경사항을 감지하여 UPDATE 쿼리를 자동으로 실행한다.

Lazy Loading

@ManyToOne과 같이 다대일 관계를 가진 Entity를 조회할 때, 실제로 해당 Entity의 데이터에 직접 접근하는 시점에 쿼리를 실행하는 방식을 의미한다. Lazy Loading을 통해 EntityManager는 불필요한 쿼리를 최소화한다.

EntityManagerFactory

EntityManagerFactoryEntityManager를 생성하는 클래스이다. EntityManager의 생성방식은 크게 Container-Manager 방식과 Application-Managed 구분된다.

Container-Managed

스프링 컨테이너에 등록된 EntityManagerFactory에서 EntityManager를 생성하여 주입하는 방식을 의미한다.

@PersistenceContext
EntityManager entityManager;

또한, Container-Managed 방식은 Thread-Safe하다는 특징이 있다. 스프링 컨테이너는 EntityManager를 주입할 때, 실제로는 이를 감싼 프록시 객체를 주입한다. 프록시 객체는 내부적으로 Thread-Lock과 관련된 기능을 구현하고 있어 멀티 스레딩 환경에서 안전하게 사용할 수 있다.

Application-Managed

스프링 컨테이너에 등록된 EntityManagerFactory를 주입받아, 어플리케이션에서 직접 EntityManager를 생성하여 사용하는 방식을 의미한다. Container-Managed 방식 보다 EntityManager를 유연하게 관리할 수 있다는 것이 특징이다.

@Repository
public class JpaRepository {
	
    @AutoWired
    private EntityManagerFactory emf;

    public Long save(Member member) { // 1. new 상태
      EntityManager em = emf.createEntityManager();
      EntityTransaction tx = em.getTransaction();
      
      try {
          tx.begin(); 

          ...
          do somegthing 
          em.persist(member)
          ...

          tx.commit();
          }
        
      catch(Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
}


References

Guide to the Hibernate EntityManager

[JPA] 영속성 컨텍스트 (Persistence Context) 특징

profile
안녕하세요

0개의 댓글