fragile and resilient

Spring

[JPA] SimpleJpaRepository의 EntityManager는 어디서 생성될까?

Green Lawn 2024. 2. 24. 22:37

JPA를 사용하면 보통 아래와 같이 선언해서 Repository를 사용합니다. 

interface JpaTestRepository extends JpaRepository<Test, Long>, TestRepository {
}

여기서 JpaRepository 의 구현체인 SimpleJpaRepository 를 살펴보겠습니다.

 

데이터를 저장할 때 사용하는 save 메서드를 기준으로 보겠습니다.

로직을 보니 entityInformation.isNew를 확인하고 true이면 em.persist(entity)를 실행하고, false이면 em.merge(entity)를 실행합니다. (이 로직에 대한 부분은 다른 포스팅에서 다루겠습니다.)

 

여기서 em을 통해 로직을 수행하는 것을 보니 EntityManager가 굉장히 중요한 역할을 하는 것으로 보입니다.

EntityManager는 Entity를 저장하거나 수정하고 조회하는 등 Entity를 관리하는 인터페이스입니다. 

EntityManager가 어디서부터 오는지 살펴보기 위해 SimpleJpaRepository를 생성하는 곳부터 찾아봅시다.

SimpleJpaRepository를 사용하는 곳을 살펴보면, JpaRepositoryFactory.getRepositoryBaseClass 에서 SimpleJpaRepository를 선언하고 있는데요.

이를 호출하는 곳으로 올라가다보면 RepositoryFactorySupport.getRepository 를 찾을 수 있습니다.

그리고 RepositoryFactorySupport.getRepository는 RepositoryFactoryBeanSupport.afterPropertiesSet에서 호출되고 있는데요. 빈이 생성되면서 실행되는 아래 로직을 살펴보겠습니다.

여기서 297번 라인과 323번 라인을 보겠습니다.

297번 라인을 타고 들어가보면 JpaRepositoryFactoryBean.createRepositoryFactory에서 EntityManager를 RepositoryFactorySupport에 주입하여 인스턴스를 만들어서 반환하고 있습니다. 

 

EntityManager가 주입된 RepositoryFactorySupport를 통해 323 라인에서 getRepository를 호출하는데요.

getRepository의 내부 로직의 325번 라인을 살펴보겠습니다. 

getTargetRepository 들어가면 아래 메서드가 나오는데요. 

내부 로직을 보면 EntityManager를 주입하고 리플렉션으로 SimpleJpaRepository를 생성할 것 이라는 것을 예상할 수 있습니다.

결국 이곳에서 SimpleJpaRepository에 EntityManager를 주입하는 것입니다.


그렇다면 여기서 두 번째 의문이 듭니다. 앞에서 JpaRepositoryFactoryBean.createRepositoryFactory 에서 EntityManager를 넣어준다는 것은 알게 되었습니다.

그럼 여기서 넣어주는 EntityManager는 어디서 생성되는 것일까요? EntityManager를 최초로 생성하는 곳이 궁금해집니다.

JpaRepositoryFactoryBean을 보면 EntityManager는 setEntityManager로 주입될 것 같다는 예상이 되는데요.

메서드 위에 @PersistenceContext 애노테이션이 눈에 띕니다. 

 

PersistenceContext를 사용하는 곳을 하나씩 확인해보면, PersistenceAnnotationBeanPostProcessor.buildPersistenceMetadata 메서드가 눈에 들어옵니다.

로직을 살펴보면 PersistenceContext 애노테이션이 붙은 경우에는 413, 432 에서 PersistenceElement라는 객체를 만들어 InjectionMetadata를 만들어서 반환하고 있는 것을 알 수 있습니다.

 

그리고 InjectionMetadata를 사용하는 곳을 찾으면 PersistenceAnnotationBeanPostProcessor.postProcessProperties가 나오는데요.

InjectionMetadata.inject를 타고 들어가보면 method.invoke가 보입니다. 

리플렉션을 통해 특정 메서드를 실행할 것이라는 것이 예상되고, 파라미터로 전달해주는 getResourceToInject를 살펴보겠습니다.

resolveEntityManager를 들어가서 SharedEntityManagerCreator.createSharedEntityManager로 가보면,

메서드 구현의 마지막 부분을 보니 Proxy.newProxyInstance 통해 EntityManager를 프록시로 감싸서 생성하고 있습니다.

프록시에서 어떤 로직을 수행하는지 보기 위해 사진의 맨 마지막 라인 SharedEntityManagerInvocationHandler의 invoke 메서드를 보겠습니다.

doGetTransactionalEntityManager의 구현 로직에서 emf(EntityManagerFactory).createEntityManager를 들어가보면

SessionFactoryImpl.createEntityManager > SessionFactoryImpl.buildEntityManager 에서

Session 즉, EntityManager를 생성하고 있는 것을 알 수 있습니다.

EntityManager는 thread-safe 하지 않기 때문에 스레드마다 할당해야 한다는 사실만 알고 있었는데요.

이처럼 프록시를 통해 새로운 EntityManager를 생성할 수 있었던 것이었습니다.

 

결론 

  • JpaRepository의 구현체인 SimpleJpaRepository는 @PersistenceContext를 통해 EntityManager를 주입받고 있다.
  • @PersistenceContext 를 선언하면 프록시를 통해 새로운 EntityManager가 할당된다.

 

References

https://github.com/spring-projects/spring-data-jpa

https://github.com/spring-projects/spring-framework

https://github.com/spring-projects/spring-data-commons

https://github.com/hibernate/hibernate-orm