우선 EntityManager에 대해 자세히 알아보도록 하겠습니다.

 

EntityManger

EntityManager는 JPA(Java Persistence API)의 핵심 인터페이스로, 애플리케이션과 데이터베이스 간의 상호작용을 관리합니다. EntityManager는 엔티티 객체의 CRUD(Create, Read, Update, Delete) 작업을 수행하고, 트랜잭션을 관리하며, JPQL(Java Persistence Query Language) 쿼리를 실행하는 역할을 합니다.

 

 

EntityManager의 주요 특징

출처 : https://girawhale.tistory.com/122

  • 엔티티의 생명 주기 관리
    • Persist: 새로운 엔티티를 영속성 컨텍스트에 추가하여 데이터베이스에 삽입합니다.
    • Merge: 영속성 컨텍스트에 이미 존재하는 엔티티를 업데이트하거나, 존재하지 않으면 새로운 엔티티로 병합합니다.
    • Remove: 영속성 컨텍스트에서 엔티티를 제거하여 데이터베이스에서도 삭제합니다.
    • Find: 주어진 기본 키로 데이터베이스에서 엔티티를 검색합니다.
    • GetReference: 주어진 기본 키로 프록시 객체를 반환하여 지연 로딩을 지원합니다.
    • 상태
      • 비영속 (New/Transient)
        • 영속성 컨텍스트가 모르는 상태
        • 엔티티 객체가 생성되고, JPA와는 아무런 관계가 없는 상태입니다.
        • 즉, 영속성 컨텍스트나 데이터베이스와는 관련이 없습니다.
        • 예를 들어, new 키워드를 사용하여 객체를 생성한 직후가 비영속 상태입니다.
      • 영속 (Managed)
        • 영속성 컨텍스트에 저장되고, 기억되고 있는 상태
        • 엔티티가 영속성 컨텍스트에 관리되고 있으며, 엔티티 매니저에 의해 추적되고 있는 상태입니다.
        • 엔티티를 영속 상태로 만들려면 EntityManager의 persist() 메서드를 호출하거나, 쿼리를 실행하여 데이터베이스로부터 엔티티를 조회할 때 발생합니다.
      • 준영속 (Detached)
        • 영속 상태였다가 영속성 컨텍스트가 더 이상 기억하지 않는 상태
        • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태입니다.
        • 즉, 영속성 컨텍스트가 더 이상 해당 엔티티를 추적하지 않습니다.
        • 주로 트랜잭션이 종료되거나, EntityManager의 detach() 메서드를 호출하여 엔티티를 분리한 경우에 발생합니다.
        • 메모리에 일시적으로 유지되며, 일 반적으로 해당 엔티티가 더 이상 사용되지 않거나, 메모리 해제 등의 이유로 가비지 컬렉터에 의해 제거됩니다.
      • 삭제 (Removed)
        • 영속성 컨텍스트에 엔티티가 삭제될거라고 기록한 상태. 즉, 삭제되는걸 기다리는 상태
        • 엔티티가 삭제되었음을 나타내는 상태입니다. 영속성 컨텍스트에서 엔티티를 제거하고 데이터베이스에서도 삭제됩니다.
        • 주로 EntityManager의 remove() 메서드를 호출하여 엔티티를 삭제한 경우에 발생합니다.
  • 영속성 컨텍스트 관리
    • Flush: 영속성 컨텍스트의 변경 사항을 데이터베이스에 동기화합니다.
    • Clear: 영속성 컨텍스트를 비워 모든 관리된 엔티티를 분리합니다.
    • Refresh: 데이터베이스의 최신 상태로 엔티티를 다시 로드합니다.
    • Detach: 특정 엔티티를 영속성 컨텍스트에서 분리합니다.
  • 쿼리 실행
    • createQuery: JPQL(Java Persistence Query Language) 쿼리를 실행하기 위해 Query 객체를 생성합니다.
    • createNamedQuery: 네임드 쿼리를 실행하기 위해 Query 객체를 생성합니다.
    • createNativeQuery: 원시 SQL 쿼리를 실행하기 위해 Query 객체를 생성합니다.
  • 트랜잭션 관리:
    • EntityManager는 트랜잭션 경계를 설정하고 관리할 수 있습니다.
    • 트랜잭션 경계를 명확히 지정하여 데이터의 일관성과 무결성을 보장합니다.
  • 캐싱
    • 1차 캐시(영속성 컨텍스트 수준)
    • EntityManager 내에서 관리되는 엔티티들은 1차 캐시에 저장됩니다. 동일한 EntityManager 내에서 동일한 엔티티를 반복해서 검색하면, 데이터베이스 조회 없이 1차 캐시에서 가져옵니다.
  • 스레드 안정성
    • EntityManager는 기본적으로 Thread-safe하지 않습니다.
    • 각 스레드마다 별도의 EntityManager 인스턴스를 사용해야 하며, 여러 스레드가 동일한 EntityManager 인스턴스를 공유하지 않도록 주의해야 합니다.
  • 플러그인 지원
    • JPA 구현체(Hibernate, EclipseLink 등)에 따라 EntityManager의 기능이 확장될 수 있으며, 특정 구현체에 특화된 기능을 제공할 수 있습니다.

EntityManger 자체적으로는 Thread-safe 하지 않습니다.(스레드 안정성) 이는 내부적으로 유지하는 상태와 데이터 구조들이 여러 스레드에서 동시에 접근될 때 예상치 못한 동작이나 데이터 불일치, 무결성 문제가 발생할 수 있기 때문입니다. 즉, 내부 상태 관리, 동시성 제어 문제, 트랜잭션 경계의 혼동 등으로 인해 여러 스레드에서 동시에 접근할 때 예기치 않은 동작이나 데이터 불일치가 발생할 수 있습니다.

 

내부 상태 관리

EntityManager는 여러 가지 내부 상태를 관리합니다:

  • Persistence Context(영속성 컨텍스트)
    • EntityManager는 영속성 컨텍스트를 통해 엔티티 인스턴스와 데이터베이스 사이의 상태를 관리합니다.
    • 이 컨텍스트는 엔티티의 상태(예: 새로 생성된 엔티티, 수정된 엔티티, 삭제된 엔티티 등)를 추적합니다.
  • 1차 캐시
    • 영속성 컨텍스트는 엔티티를 캐시하여 데이터베이스와의 상호 작용을 최적화합니다.
    • 여러 스레드가 이 캐시를 동시에 수정하려고 하면, 데이터 불일치가 발생할 수 있습니다.
  • 트랜잭션 관리
    • EntityManager는 트랜잭션과 관련된 상태를 관리합니다.
    • 여러 스레드가 동일한 EntityManager 인스턴스를 사용하여 트랜잭션을 시작하고 커밋하거나 롤백하려고 하면, 트랜잭션 경계가 혼동될 수 있습니다.

동시성 제어 문제

EntityManager는 다양한 동작을 수행합니다:

  • 엔티티 조회
    • 여러 스레드가 동일한 엔티티를 조회하고 수정하려고 할 때, 동시성 문제가 발생할 수 있습니다.
  • 엔티티 변경
    • 엔티티의 상태를 변경할 때, 변경 내용이 충돌하거나 덮어쓰여질 수 있습니다.
  • 플러시(flush) 작업
    • EntityManager는 트랜잭션이 커밋될 때 또는 명시적으로 플러시가 호출될 때 변경 사항을 데이터베이스에 반영합니다.
    • 여러 스레드가 플러시를 동시에 호출하면, 예상치 못한 결과가 발생할 수 있습니다.

트랜잭션 경계의 혼동

  • 트랜잭션 시작 및 종료
    • EntityManager 인스턴스는 트랜잭션 경계를 정의하는데 사용됩니다.
    • 동일한 EntityManager를 여러 스레드가 사용하면, 한 스레드에서 시작된 트랜잭션이 다른 스레드에 영향을 미칠 수 있습니다.
  • 트랜잭션 롤백
    • 하나의 스레드가 트랜잭션을 롤백하면, 다른 스레드의 작업이 예기치 않게 취소될 수 있습니다.

이러한 이유로 인해 EntityManager는 Thread-safe 하지 않습니다.


그러면 어떻게 Thread-safe을 보장받을수 있는지에 대해 조사하였습니다.

 

트랜잭션 범위 내에서 사용

  • 각 트랜잭션 범위 내에서 EntityManager를 사용합니다.
  • 각 트랜잭션마다 별도의 EntityManager를 생성하고, 트랜잭션이 종료될 때 EntityManager를 닫습니다.
  • 이를 통해 각 트랜잭션은 자체적으로 EntityManager를 보유하게 되며, 서로 간섭하지 않습니다.

Spring의 @Transactional 애노테이션 사용

  • 스프링 프레임워크를 사용한다면, @Transactional 애노테이션을 트랜잭션 범위 내에서 메서드에 적용하여 EntityManager를 관리할 수 있습니다.
  • 스프링은 @Transactional이 적용된 메서드 내에서 EntityManager를 생성하고 관리하며, 메서드의 실행이 완료되면 EntityManager를 닫습니다.
  • 트랜잭션 전파 수준과 격리 수준을 설정하여 다양한 트랜잭션 시나리오를 지원할 수 있습니다.
  • 이를 통해 thread-safe하게 데이터베이스 작업을 수행하고, 데이터 일관성을 보장할 수 있습니다.

Synchronized 메서드 또는 블록 사용

  • EntityManager에 대한 접근을 동기화된 메서드나 블록 내에서 수행하여 Thread-safe를 보장할 수 있습니다.
  • 이를 통해 여러 스레드가 동시에 EntityManager에 접근하는 것을 방지할 수 있습니다.

Thread-local EntityManager 사용

  • 각 스레드마다 별도의 EntityManager 인스턴스를 사용하여 영속성 컨텍스트를 관리합니다.
  • 각 스레드 내에서 thread-local 변수를 통해 EntityManager를 관리하여 스레드 간의 공유를 방지합니다.

참고

'JAVA & Spring > Spring' 카테고리의 다른 글

JPA의 동작 원리  (0) 2024.05.20
JPA 구성요소와 기본 이해  (0) 2023.09.18
[Spring]DI(의존성 주입)  (0) 2022.04.14

+ Recent posts