본문 바로가기
웹 개발/jpa

[jpa] JPA구동 방식 + EntityManager, EntityManagerFactory 란?

by dani0312 2023. 12. 19.

JPA를 이해하기 위해 필요한 것 중 하나가 엔티티매니저와 엔티티매니저팩토리이다.

이번 글에서는 JPA구동 방식과 엔티티매니저, 엔티티매니저팩토리가 무엇인지에 대해 알아볼 것이다.

 

📌JPA 동작

◾JPA 구동 방식

JPA구동 방식은 아래와 같다. JPA는 Persistence라는 클래스가 존재한다.

1. Persistence클래스에서 META-INF/persistence.xml라 하는 설정 정보를 읽는다.

2. EntityManagerFactory라는 클래스를 만든다.

3. EntityManagerFactory에서 필요할 때마다 EntityManger라는 것을 만들어 동작한다.

 

persistence.xml은 설정 파일이다. JPA구현체에서 어떻게 동작해야 하는지에 대한 정보를 제공한다. 그렇다면 EntityManager와 EntityManagerFactory란 무엇일까? 다음 단락을 참조하자

 

 

 

📌EntityManager와 EntityManagerFactory

◾EntityManager란?

이름 그대로 Entity를 관리하는 역할을 하는 클래스라고 보면 된다. EntityManager가 엔티티들을 어떻게 관리할까? EntityManager가 생성될 때 안에 영속성 컨텍스트라는 것을 함께 생성해 이것을 통해 관리한다. 영속성 컨텍스트에 대한 설명은 다음 포스팅을 참조하자. EntityManager에 대해 정리하면 아래와 같다.

 

EntityManager 특징

💧 엔티티를 관리하는 역할을 한다

💧 JPA의 대부분의 기능을 제공한다

💧 엔티티를 DB에 CRUD할 수 있게 한다

💧 엔티티 매니저는 쓰레드간에 공유하면 안된다. (아래 설명 참조)

💧 JPA의 모든 데이터 변경은 트랜잭션 안에서 실행 (아래 설명 참조)

 

 

엔티티 매니저는 DB커넥션을 유지하면서 DB와 통신을 할 정도로 밀접한 관계가 있으므로 스레드 간에 절대 공유하거나 재사용하면 안된다. 사용하고 버려야한다. 자세한 설명은 아래 더보기를 참조하자

 

더보기

💡EntityManager 를 스레드 간에 공유하면 안되는 이유

 

1. 영속성 컨텍스트의 동시성 문제

각각의 엔티티 매니저는 자체 영속성 컨텍스트를 가지고 있다. 영속성 컨텍스트는 엔티티의 상태를 추적하고 캐싱하는데 사용된다. 스레드 간에 영속성 컨텍스트를 공유하면 여러 스레드에서 동시에 영속성 컨텍스트를 수정하려 할 수 있다. 이로 인해 예상치 못한 문제가 발생할 수 있다.

 

2. 트랜잭션 관리의 엉려움

엔티티 매니저는 트랜잭션을 관리하는 역할도 수행한다. 하나의 스레드에서 시작된 트랜잭션은 해당 스레드에서만 커밋 또는 롤백되어야 한다. 스레드 간에 엔티티 매니저를 공유하면 트랜잭션의 범위와 커밋 또는 롤백이 예상치 못한 방식으로 처리될 수 있다

 

3. 캐시와 일관성 문제 

각각의 영속성 컨텍스트는 캐시를 가지고 있어 자주 사용되는 엔티티를 저장해두고 다시 사용할 수 있다. 하지만 스레드 간에 엔티티 매니저를 공유하면 캐시 관리가 어려워져 예상치 못한 결과를 초래할 수 있다

 

따라서 각각의 스레드에서 독립적인 엔티티 매니저 인스턴스를 사용해야 한다 스레드 간에 데이터를 공유하려면 올바른 동시성 제어 메커니즘을 사용하여 데이터의 일관성과 안전성을 보장해야 한다 

 

 

엔티티 매니저의 메서드들을 이용해 데이터를 변경하는 작업은 트랜잭션 안에서 실행해야한다. 그리고 변경이 완료된 후에 트랜잭션을 커밋하여 데이터베이스에 실제로 반영된다. 커밋 전에는 쿼리가 생성되지 않는다 즉 트랜잭션을 커밋할 때까지 데이터베이스에 실제로 변경 사항이 반영되지 않는다. 

 

(엔티티 매니저의 메서드들을 활용한 CRUD에 대해서는 본 글의 가장 아래의 예제 코드를 참조할 수 있다)

 

 

 

◾EntityManagerFactory

엔티티 매니저는 사용 후 버려야 한다고 했다. 즉 요청이 올때마다 엔티티매니저는 계속 생성이 되어야한다. 이것을 누가 해줄까? EntityManagerFactory이다. 이름 그대로 엔티티매니저를 만들어내는 공장이라고 보면된다. 엔티티 매니저를 생성하고 관리하는 역할을 한다.   엔티티 매니저는 여러 스레드가 동시에 접근해도 안전하다. 단순히 엔티티 매니저만 만들기 때문이다.  엔티티 매니저는 보통 애플리케이션의 라이프사이클 동안 단일 인스턴스(1개)만 생성된다.

 

엔티티 매니저와 엔티티 매니저 팩토리에 대해 알아보았다 웹 애플리케이션을 개발한다면 고객 요청이 올 때 이 두 개가 어떻게 동작하는 가는 아래와 같다.

 

1. 고객의 요청이 온다

2. EntityManagerFactory가 EntityManager를 생성한다

3. EntityManager는 데이터베이스 커넥션을 사용해 DB를 사용하게 된다

 

다음은 엔티티 팩토리를 생성해 이를 통해 엔티티 매니저를 생성하는 코드이다.

 

        //persistence-unit의 name="hello" 인자로 전달
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

 

 

엔티티 매니저와 엔티티 매니저 팩토리를 이해했다면 이들과 더불어 동작하는 영속성 컨텍스트는 무엇일까? JPA를 이해하는 데 중요한 역할을 하는 녀석이다. 이는 다음 글에서 알아볼 예정이다. 아래는 앞서 설명한 내용에 대한  jpa예제 코드이다 

 

 

◾EntityManagerFactory, EntityManager를 이용한 JPA 예제 코드

public static void main(String[] args) {
        //persistence-unit의 name="hello"
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin(); // 트랜잭션 안에서 작업을 해야한다.

        try {
            //등록
//            Member member = new Member();
//            member.setId(2L);
//            member.setName("hello!JPAAAA");
//            em.persist(member);

            //조회 find()메서드
//            Member findMember = em.find(Member.class, 1L);
            //수정
//            findMember.setName("헨노우 제이피에이");

            //삭제 remove()메서드
//            em.remove(member);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close(); // 엔티티 매니저 팩토리 종료. 애플리케이션을 종료 시 emf도 종료해야한다.
    }

코드를 보면 트랜잭션을 시작하고(tx.begin()) 엔티티매니저를 이용해 db작업에 대한 코드를 작성한다. 그리고 트랜잭션을 이용해 커밋한다(실제 이 시점에 데이터베이스에 변경이 일어난다)

em.persist()시점이 아닌 트랜잭션을 커밋하는 시점에 데이터베이스 변경이 일어남을 기억하자. 이 부분은 영속성 컨텍스트와 관련이 있는데 이는 다음 포스팅에서 할 예정이다

 

엔티티 매니저는 사용 후 반드시 종료한다. 전체 애플리케이션을 종료 시에는 엔티티매니저팩토리도 종료한다.

 

 

 

 


자바 ORM 표준 JPA 김영한


/* 내가 추가한 코드 */ /* 내가 추가한 코드 끝끝 */