EntityManager

  • 일반적으로 DataSource가 하나라면 EntityManagerFactory를 하나만 생성한다.
  • 우리가 사용하는 EntityManager는 EntityMangerFactory로부터 만들어 나온다. 비용은 거의 들지 않는다.
  • EntityMangerFactory는 여러 쓰레드가 동시에 접근하면 동시성 문제가 발생하므로 쓰레드 간 공유는 하지 말아야 한다.
  • EntityManager는 문제가 없다.

EntityManagerFactor

  • hiberante에서는 public interface SessionFactory extends EntityManagerFactory, Referenceable, Serializable, Closeable와 같이 SessionFactory가 상속을 받아서 구현하고 있다.
public class Persistence {
   /** @deprecated */
   @Deprecated
   public static final String PERSISTENCE_PROVIDER = "jakarta.persistence.spi.PersistenceProvider";
   /** @deprecated */
   @Deprecated
   protected static final Set<PersistenceProvider> providers = new HashSet();

   public Persistence() {
   }

   public static EntityManagerFactory createEntityManagerFactory(String persistenceUnitName) {
       return createEntityManagerFactory(persistenceUnitName, (Map)null);
   }
//Provider에 맞춰서 EntityManagerFactoryBuilderImpl로 EntityManagerFactory를 생성한다.

   public static EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) {
         EntityManagerFactory emf = null;
         PersistenceProviderResolver resolver = PersistenceProviderResolverHolder.getPersistenceProviderResolver();
         List<PersistenceProvider> providers = resolver.getPersistenceProviders();
         Iterator var5 = providers.iterator();
 
         while(var5.hasNext()) {
             PersistenceProvider provider = (PersistenceProvider)var5.next();
             emf = provider.createEntityManagerFactory(persistenceUnitName, properties);
             if (emf != null) {
                 break;
             }
         }
 
         if (emf == null) {
             throw new PersistenceException("No Persistence provider for EntityManager named " + persistenceUnitName);
         } else {
             return emf;
         }
     }
  }

public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl implements SessionFactoryImplementor {
 public Session createEntityManager() {
     this.validateNotClosed();
     return this.buildEntityManager(SynchronizationType.SYNCHRONIZED, (Map)null);
 }

 private <K, V> Session buildEntityManager(SynchronizationType synchronizationType, Map<K, V> map) {
     assert this.status != SessionFactoryImpl.Status.CLOSED;
     SessionBuilderImplementor builder = this.withOptions();
     ((SessionBuilderImplementor)builder).autoJoinTransactions(synchronizationType == SynchronizationType.SYNCHRONIZED);
     if (map != null) {
         Object tenantIdHint = map.get("org.hibernate.tenantId");
         if (tenantIdHint != null) {
             builder = (SessionBuilderImplementor)((SessionBuilderImplementor)builder).tenantIdentifier(tenantIdHint);
         }
     }
     Session session = ((SessionBuilderImplementor)builder).openSession();
     if (map != null) {
         Iterator var5 = map.entrySet().iterator();
         while(var5.hasNext()) {
             Map.Entry<K, V> o = (Map.Entry)var5.next();
             K key = o.getKey();
             if (key instanceof String) {
                 String sKey = (String)key;
                 if (!"org.hibernate.tenantId".equals(sKey)) {
                     session.setProperty(sKey, o.getValue());
                 }
             }
         }
     }
     return session;
 }
}

영속성 컨텍스트

  • 엔티티를 영구 저장하는 환경으로 이해하면 된다.
  • InnoDB의 버퍼풀(언두/리두 로그를 포함해서)과 아이디어적으로 비슷해 보인다. (쓰기 버퍼링, 풀 데이터를 바꾸고 언두에 기록하는 행위 등을 생각했을 때?)

생명 주기

  1. 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
  2. 영속(managed) : 영속성 컨텍스트에 저장된 상태
  3. 준영속(detached) : 영속성 컨텍스트에 있다가 분리
  4. 삭제(removed) : 삭제

entityManager.png

영속성 컨텍스트의 장점

  • 1차 캐시 : 영속성 컨텍스트 내부 캐시 (우선 1차 캐시에서 찾고 없으면 DB)
  • 동일성 보장 : 영속성 컨텍스트에서 관리되고 있다면 항상 같은 엔티티 인스턴스 반환
  • 트랜잭션을 지원하는 쓰기 지연 : INSERT 시 트랜잭션 커밋할 때 모아둔 쿼리를 한 번에 보낸다. (이 부분도 버퍼풀 + 언두 로그를 이용한 쓰기 버퍼링 동작과 비슷하다.)
  • 변경 감지(dirtyChecking) : 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다. (단, 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용)
  • 지연 로딩 : 프록시 객체를 로드해 두고 필요할 때 영속성 컨텍스트를 통해서 데이터를 불러오는 방법이다.