JPA구동 방식은 아래와 같다. JPA는 Persistence라는 클래스가 존재한다.
Persistence클래스에서 META-INF/persistence.xml라 하는 설정 정보를 읽는다.
EntityManagerFactory라는 클래스를 만든다.
EntityManagerFactory에서 필요할 때마다 EntityManger라는 것을 만들어 동작한다.
persistence.xml은 설정 파일이다. JPA구현체에서 어떻게 동작해야 하는지에 대한 정보를 제공한다. 그렇다면 EntityManager와 EntityManagerFactory란 무엇일까? 다음 단락을 참조하자
이름 그대로 Entity를 관리하는 역할을 하는 클래스라고 보면 된다. EntityManager가 엔티티들을 어떻게 관리할까?
EntityManager가 생성될 때 안에 영속성 컨텍스트라는 것을 함께 생성해 이것을 통해 관리한다. 영속성 컨텍스트에 대한 설명은 다음 포스팅을 참조하자. EntityManager에 대해 정리하면 아래와 같다.
💧 엔티티를 관리하는 역할을 한다
💧 JPA의 대부분의 기능을 제공한다
💧 엔티티를 DB에 CRUD할 수 있게 한다
💧 엔티티 매니저는 쓰레드간에 공유하면 안된다. (아래 설명 참조)
💧 JPA의 모든 데이터 변경은 트랜잭션 안에서 실행 (아래 설명 참조)
엔티티 매니저는 DB커넥션을 유지하면서 DB와 통신을 할 정도로 밀접한 관계가 있으므로 스레드 간에 절대 공유하거나 재사용하면 안된다. 사용하고 버려야한다. 자세한 설명은 아래 더보기를 참조하자
💡 EntityManager를 스레드 간에 공유하면 안 되는 이유
### 1. 영속성 컨텍스트의 동시성 문제
각각의 엔티티 매니저는 자체 영속성 컨텍스트를 가지고 있습니다. 영속성 컨텍스트는 엔티티의 상태를 추적하고 캐싱하는 데 사용됩니다. 스레드 간에 영속성 컨텍스트를 공유하면 여러 스레드에서 동시에 수정하려 할 수 있어 데이터 정합성이 깨질 수 있습니다.
### 2. 트랜잭션 관리의 어려움
하나의 스레드에서 시작된 트랜잭션은 해당 스레드에서만 커밋 또는 롤백되어야 합니다. 엔티티 매니저를 공유하면 트랜잭션의 범위가 모호해져 예상치 못한 커밋이나 롤백이 발생할 수 있습니다.
### 3. 캐시와 일관성 문제
각 영속성 컨텍스트의 1차 캐시는 스레드별로 독립적이어야 합니다. 공유 시 캐시 관리가 불가능해져 데이터 일관성을 보장할 수 없습니다.
**결론:** 따라서 각각의 스레드에서 독립적인 엔티티 매니저 인스턴스를 사용해야 합니다.
엔티티 매니저의 메서드들을 이용해 데이터를 변경하는 작업은 트랜잭션 안에서 실행해야한다. 그리고 변경이 완료된 후에 트랜잭션을 커밋하여 데이터베이스에 실제로 반영된다. 커밋 전에는 쿼리가 생성되지 않는다 즉 트랜잭션을 커밋할 때까지 데이터베이스에 실제로 변경 사항이 반영되지 않는다.
엔티티 매니저는 사용 후 버려야 한다고 했다. 즉 요청이 올때마다 엔티티매니저는 계속 생성이 되어야한다. 이것을 누가 해줄까? EntityManagerFactory이다. 이름 그대로 엔티티매니저를 만들어내는 공장이라고 보면된다. 엔티티 매니저를 생성하고 관리하는 역할을 한다. 엔티티 매니저는 여러 스레드가 동시에 접근해도 안전하다. 단순히 엔티티 매니저만 만들기 때문이다. 엔티티 매니저는 보통 애플리케이션의 라이프사이클 동안 단일 인스턴스(1개)만 생성된다.
엔티티 매니저와 엔티티 매니저 팩토리에 대해 알아보았다 웹 애플리케이션을 개발한다면 고객 요청이 올 때 이 두 개가 어떻게 동작하는 가는 아래와 같다.
고객의 요청이 온다
EntityManagerFactory가 EntityManager를 생성한다
EntityManager는 데이터베이스 커넥션을 사용해 DB를 사용하게 된다
다음은 엔티티 팩토리를 생성해 이를 통해 엔티티 매니저를 생성하는 코드이다.
//persistence-unit의 name="hello" 인자로 전달
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
엔티티 매니저와 엔티티 매니저 팩토리를 이해했다면 이들과 더불어 동작하는 영속성 컨텍스트는 무엇일까? JPA를 이해하는 데 중요한 역할을 하는 녀석이다. 이는 다음 글에서 알아볼 예정이다. 아래는 앞서 설명한 내용에 대한 jpa예제 코드이다
//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()시점이 아닌 트랜잭션을 커밋하는 시점에 데이터베이스 변경이 일어남을 기억하자. 이 부분은 영속성 컨텍스트와 관련이 있는데 이는 다음 포스팅에서 할 예정이다
Reference
자바 ORM 표준 JPA 김영한