Spring Bean으로 등록된 EntityManager

2023. 5. 12. 10:44·개발/JPA
반응형

자바 동적 프록시 기술을 알고 읽으시는 게 좋습니다!!!

 

JPA를 처음에 배우면 엔티티 매니저는 여러 스레드가 동시에 접근하므로 스레드 간 공유하면 절대 안된다고 학습한다.

그러나 스프링 부트 애플리케이션을 돌려보면 처음에 엔티티 매니저가 스프링 빈으로 등록되는 것을 확인할 수 있다.

 

'스프링 부트 자동 구성을 통해 엔티티 매니저가 스프링 빈으로 등록, 즉 싱글톤으로 사용이 되는 거 같아'

 

위와 같은 의문점이 들었고, 이에 관련해서 구글에 찾아보기 시작했다.

결론을 말하면 스프링 빈으로 등록되는 것은 엔티티 매니저 가짜 객체고 실제 애플리케이션에서 로직이 수행될 때 진짜 엔티티 매니저가 사용된다는 것이다. 이러면 쓰레드 세이프하다고 말할 수 있다.

 

단순히 그렇구나 하고 넘어간 것을 오늘은 눈으로 확인해보려고 한다. 

작성한 초간단 테스트는 아래와 같다. 엔티티 매니저 인스턴스 이름을 em이라고 정하고 진행했다.

@SpringBootTest
public class Tests{

    @Autowired
    EntityManager em;

    @Transactional
    @Test
    void test(){
        Survey e = SurveyFactory.create(); //엔티티 생성
        em.persist(e);
    }
}

 테스트 코드 em.persist()에서 디버깅을 시작해보자.

그럼 맨 처음 아래와 같은 디버깅 창을 확인할 수 있다.

디버깅 시작

h 인스턴스 변수는 자바 동적 프록시 클래스 에서 InvocationHandler를 가리키는 변수다.

현재 상황을 정리하면 스프링 빈으로 등록된 엔티티 매니저는 프록시로 등록되고, 프록시가 어떤 InvocationHandler를 호출하는 것이다.

 

그 어떤 Invocation Handler가 바로 SharedEntityManagerCreator 클래스 내부에 있는

private static 클래스 SharedEntityManagerInvocationHandler다.

 

일단 SharedEntityManagerCreator와 SharedEntityManagerInvocationHandler를 정리해보자.

SharedEntityManagerCreator

  • EntityManagerFactory로부터 공유 가능한 EntityManager를 만들기 위해 사용되는 대리자를 의미한다.
  • 현재 트랜잭션 EntityManager에 대한 모든 호출을 위임하고, 그렇지 않은 경우에는 각각의 작업마다 새로운 EntityManager를 생성한다.
  • 공유 트랜잭션 EntityManager의 동작 방식에 대한 행동 정의는 javax.persistence.PersistenceContextType.TRANSACTION과 JPA 명세서에서 논의하는 대로 작동한다.
  • 애노테이션 기반 javax.persistence.PersistenceContext.type()의 기본값으로 사용된다.

SharedEntityManagerInvocationHandler

  • 현재 트랜잭션이 존재하면 모든 호출을 해당 트랜잭션에 속한 EntityManager로 위임한다.
  • 만약 그렇지 않으면 각각의 작업마다 새로운 EntityManager를 생성하는 호출 핸들러(Invocation handler)를 말한다.
  • 즉, 핸들러는 EntityManager의 wrapper 역할을 하며, EntityManagerFactory를 사용하여 EntityManager를 생성한다.
  • 현재 트랜잭션이 활성화된 경우, 핸들러는 해당 트랜잭션에 속한 EntityManager를 반환한다.
  • 그렇지 않은 경우, 핸들러는 새로운 EntityManager를 생성하여 각각의 작업에 사용한다.

개념을 정리했으니 디버깅을 계속 진행해보자. InvocationHandler의 invoke 메서드를 따라가자.

그럼 아래와 같은 구간에서 멈춘다.

참고로 현재 targetFactory도 InvocationHandler지만 내부에 타겟은 LocalContainerEntityManagerFactoryBean 인스턴스다.

 

그럼 다시 내부를 탐색해보자.

위 코드에서 트랜잭션 동기화 매니저에서 EntityManager를 가지고 있는 EntityManagerHolder 인스턴스를 반환한다.

이 인스턴스에 EntityManager 구현체인 SessionImpl이 들어가있다.

 

여기에서 우리가 원하는 진짜 EntityManager 구현체를 가져온 것이다.

 

타고 온김에 EntityManger가 쿼리를 실행하는 부분이 궁금해졌다.

계속해서 persist() 메서드를 호출하는 부분을 찾아보자.

코드를 타고 내려오면서 method.invoke() 내부로 들어간다.

그럼 SessionImpl의 클래스의 persist 메서드에 도달한다.

사실 여기까지 도달하면 끝났다고 생각할 수 있지만, 계속 파고 들어가봤다.

 

그러다가 아래와 같이 saveWithGeneratedId라는 메서드를 만났다.

이 메서드를 타고 들어가면 먼저 임시 엔티티를 만들어서 변경 감지를 위한 추적을 세팅해주고, 엔티티에 할당할 id를 먼저 생성한다.

그리고 내부를 더 타고 들어가다보면 영속성 컨텍스트에 엔티티를 저장하는 코드가 나온다.

 

이후에 엔티티를 DB에 저장하기 위한 코드가 나온다. addInsertAction() 메서드를 살펴보겠다.

 

addInsertAction에서 엔티티 저장과 관련된 정보가 담긴 EntityInsertAction을 만들고,

이 액션을 EntityManager의 구현체인 SessionImpl의 ActionQueue에 insert 이벤트를 넣어두는 것이다.

 

이제 flush 흐름을 따라가면 엔티티, 콜렉션을 더티 체킹과 같은 flush와 연관 있는 코드가 나온다.

ActionQueue에서 insert 이벤트를 꺼내서 실행한다. 코드는 아래와 같다.

actionQueue.executeActions();

 

그럼 이제 데이터베이스에 반영이 된다.

참고

현재는 EntityManger의 메서드인 persist 내부를 확인하는 중이다. 
EntityManager 인스턴스 생성은 테스트 코드를 처음 시작할 때 초기화하는 과정을 확인했다.

처음 테스트 코드를 시작할 때 진짜 엔티티 매니저가 만들어지는 부분.

 

이렇게 Spring Bean으로 등록되는 EntityManager와 EntityManager가 쿼리를 실행하는 내용을 살펴보았습니다.

틀린 부분이나 피드백 환영입니다!!! 감사합니다.

 

 

 

 

반응형
'개발/JPA' 카테고리의 다른 글
  • @Version을 사용하지 않는데 ObjectOptimisticLockingFailureException 등장?!
  • 낙관적 락, 비관적 락, 네임드 락
  • Spring Data JPA 정리
  • 스프링 부트와 JPA 활용 정리
Debin
Debin
공부 기록을 남기며 게시글 리팩토링을 진행하는 블로그입니다.
  • Debin
    리팩토링하는 블로그
    Debin
  • 전체
    오늘
    어제
    • 분류 전체보기
      • DB
        • DB 기초
        • MySQL
        • SQL 튜닝
      • OS
      • Network
      • Git
      • 디지털콘텐츠기획
      • 소프트웨어공학
      • 코딩테스트
        • 프로그래머스
        • 백준
        • 인프런
      • 공부 일지
      • 독서
        • 클린코드
        • 일상 속 사물이 알려주는 웹 API 디자인
        • 토비의 스프링
        • 객체지향의 사실과 오해
        • 자바 잘 읽는 법
      • 기록 및 회고
      • Cloud
        • AWS
      • 개발
        • Java
        • Spring Core
        • Spring MVC
        • Spring DB
        • Spring Boot
        • Spring Security
        • Spring Batch
        • JPA
        • Test
        • Android
      • 대외활동
        • UMC SERVER
        • 카엔프 SW 아카데미
      • 프로젝트
      • Docker
      • Gradle
      • ELK
      • 실무 이야기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

    • 본인 깃허브입니다!
  • 인기 글

  • 태그

    Java
    container
    test
    객체지향
    인덱스
    스프링
    컨테이너
    토비의 스프링
    ORM
    AWS
    코딩 #개발자 #노마드북클럽 #노개북
    도커
    리눅스
    트랜잭션
    데이터베이스
    mysql
    SQL
    객체
    spring mvc
    프록시
    spring boot
    docker
    JPA
    redis
    innodb
    AOP
    spring
    스프링 부트
    운영체제
    자바
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Debin
Spring Bean으로 등록된 EntityManager
상단으로

티스토리툴바