오늘은 Hibernate 5.0에서 재설계된 Bootstrapping API를 알아보자.
BootStrapping이란 용어는 소프트웨어 컴포넌트의 초기화 과정과 시작을 의미한다.
Hibernate에서는 JPA를 위한 완벽한 구성을 위해 SessionFactory의 instance 혹은 EntityManagerFactory instace를 빌딩 하는 과정을 이야기한다.
또한 부트스트랩 프로세스 중에 Hibernate 동작을 커스텀할 수도 있다.
즉 SessionFactory를 빌딩 한다는 것은 시작 및 런타임 동안 Hibernate가 필요로 하는 서비스를 가지고 있는 ServiceRegistry 구현을 가지도록 하는 것입니다.
그렇기 때문에 ServiceRegistry에서 애플리케이션의 도메인 모델과 데이터베이스에 대한 매핑을 나타내는 MetaData 객체를 생성할 수 있습니다.
ServiceRegistry Build
첫 번째 단계는 bootstrapping 과정에서 런타임에 하이버네이트가 필요로 하는 서비스인 ServiceRegistry를 빌딩 하는 것이다.
하이버네이트에서는 BootstrapServiceRegistry와 StandardServiceRegistry가 존재한다.
BootstrapServiceRegistry
BootstrapServiceRegistry는 Hibernate가 부트스트랩과 런타임 모두에서 필요로 하는 서비스를 가지도록 하기 위한 것이다.
다음과 같이 3가지 서비스로 요약할 수 있다.
- ClassLoaderService : Hibernate가 ClassLoader와 상호 작용하는 방식을 정의한다.
- IntegratorService : IntegratorService는 다른 서비스나 컴포넌트를 통합하는 책임을 가지고 있습니다. 이러한 여러 구성요소들이 서로 상호작용할 수 있는 중앙집중식 방법을 제공하여 모듈화 되고
확장 가능한 아키텍처를 가능하게 합니다.
예를 들어 사용자 커스텀 유형을 추가할 수 있는 TypeContributions, 엔티티의 second-level cache를 관리하는 SecondLevelCache.. 등 이러한 서비스를 통해 유연한 구조를 제공하여 개발자가 쉽게 새로운 특징을 가진 컴포넌트를 추가할 수 있습니다. - StrategySelector : Hibernate가 다양한 전략에 대한 구현계약을 해결하는 방법을 제어합니다.
BootstrapServiceRegistry 구현을 구축하기 위해 BootstrapServiceRegistryBuilder 팩토리 클래스를
사용하여 이러한 세 가지 서비스를 유형 안전 방식으로 사용자 정의할 수 있습니다.
BootstrapServiceRegistry bootstrapServiceRegistry =newBootstrapServiceRegistryBuilder()
.applyClassLoader()
.applyIntegrator()
.applyStrategySelector()
.build();
ServiceRegistry
두 번째 ServiceRegistry는 StandardServiceRegistry로, 이전 BootstrapServiceRegistry를 부모로 하여 위에서 언급한 세 가지 서비스를 보유합니다. 또한 StandardServiceInitiators 클래스에 나열된 Hibernate에 필요한 다양한 기타 서비스를 포함합니다.
이전 레지스트리와 마찬가지로 StandardServiceRegistryBuilder를 사용하여 StandardServiceRegistry의 인스턴스를 만듭니다.
StandardServiceRegistryBuilder standardServiceRegistry =
newStandardServiceRegistryBuilder();
내부적으로 StandardServiceRegistryBuilder는 BootstrapServiceRegistry의 인스턴스를 생성하고 사용합니다. 오버로드된 생성자를 사용하여 이미 생성된 인스턴스를 전달할 수도 있습니다.
BootstrapServiceRegistry bootstrapServiceRegistry =
newBootstrapServiceRegistryBuilder().build();
StandardServiceRegistryBuilder standardServiceRegistryBuilder =
newStandardServiceRegistryBuilder(bootstrapServiceRegistry);
이 빌더를 사용하여 기본 hibernate.cfg.xml과 같은 리소스 파일에서 구성을 로드하고
마지막으로 build() 메서드를 호출하여 StandardServiceRegistry의 인스턴스를 가져옵니다.
StandardServiceRegistry standardServiceRegistry = standardServiceRegistryBuilder
.configure()
.build();
Building the Metadata
두 번째로는 org.hibernate.boot에 정의된 Metadata를 구축하는 일이다.
지금까지 BootstrapServiceRegistry 또는 StandardServiceRegistry 유형의 ServiceRegistry를 인스턴스화해서 구성했다면 이제는 애플리케이션 도메인 모델 및 해당 데이터베이스 매핑에 필요한 정보를 제공해야 합니다.
메타 데이터 객체에는 애플리케이션 도메인 모델에 대한 구문 분석된 표현과 데이터베이스에 대한 매핑을 가진다.
MetadataSoruces의 목적은 annotated class, xml files을 구문 분석할 소스의 정보이다.
ServiceRegistry standardRegistry =
new StandardServiceRegistryBuilder().build();
MetadataSources sources = new MetadataSources( standardRegistry )
.addAnnotatedClass( MyEntity.class )
.addAnnotatedClassName( "org.hibernate.example.Customer" )
.addResource( "org/hibernate/example/Order.hbm.xml" )
.addResource( "org/hibernate/example/Product.orm.xml" );
Metadata metadata = sources.buildMetadata();
혹은
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().build();
MetadataSources metadataSources = new MetadataSources(registry);
metadataSources.getMetadataBuilder()
.applyImplicitNamingStrategy(ImplicitNamingStrategyJpaCompliantImpl.INSTANCE)
.applyImplicitSchemaName("new_schema")
.build();
Metadata를 빌딩 할 때 암시적 명명전략을 설정하기도 하고 데이터베이스의 schema를 선택하기도 혹은 custom 된 Attribute Converter를 등록하기도 한다.
실제 우리는 application.properties와 같은 파일로 이러한 설정을 통해 metadata 설정을 변경하는데,
스프링 부트가 설정 정보를 읽어서 빈 구성 정보를 변경한다는 이야기다.
Building the SessionFactory
마지막으로는 설정된 metadata를 통해 SessionFactory를 생성한다.
StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
.configure( "org/hibernate/example/hibernate.cfg.xml" )
.build();
Metadata metadata = new MetadataSources( standardRegistry )
.addAnnotatedClass( MyEntity.class )
.addAnnotatedClassName( "org.hibernate.example.Customer" )
.addResource( "org/hibernate/example/Order.hbm.xml" )
.addResource( "org/hibernate/example/Product.orm.xml" )
.getMetadataBuilder()
.applyImplicitNamingStrategy( ImplicitNamingStrategyJpaCompliantImpl.INSTANCE )
.build();
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder()
.applyBeanManager( getBeanManager() )
.build();
여기까지 Hibernate 5의 부트스트랩의 과정이다.
하지만 우리는 JPA라는 표준 스펙을 사용하기에 이와 관련된 상관관계도 조금 엿볼 필요가 있다.
JPA
JPA 구현체로 Hibernate를 사용하는 경우 JPA사양을 준수하는 방식으로 부트스트랩 방식을 사용할 수 있다. 그리고 실제로 Hibernate에서도 JPA 표준 bootstrapping을 사용하는 것을 강하게 권고하고 있다.
JPA에서는 궁극적으로 javax.persistence.EntityManagerFactory 인스턴스를 부트 스트랩하는 데 관심이 있다.
JPA 스펙은 응용 프로그램이 EntityManagerFactory의 javax.persistence.EntityManager 인스턴스에 액세스 하려는 방법에 따라 두 가지 기본 표준화된 부트 스트랩 접근 방식을 정의합니다.
이 두 가지 접근 방식에 대한 용어로는 EE 혹은 SE라고 부르는데, EE는 애플리케이션을 대신하여 영속성 콘텍스트를 주입해 줄 Container가 존재하면 EE 그 외 나머지 방법은 SE라고 명명했다.
영속성 콘텍스트는 JPA의 핵심으로 엔티티를 영구 저장하는 환경으로 EntityManager와 연관관계가 있다.
공식문서를 보면 EntityManagerFactory의 bootstrapping 같은 경우에는 Annotation 또는 JNDI 조회를 통해 META-INF/persistence.xml을 주입받도록 되어 있습니다.
@PersistenceUnit
private EntityManagerFactory emf;
or
@PersistenceUnit(
unitName = "CRM"
)
private EntityManagerFactory entityManagerFactory;
구성 파일이 여러 개가 존재하는 경우 특정 단위의 이름을 unitName 속성을 통해 주입받습니다.
persistence.xml 구성파일을 살펴보죠.
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="CRM">
<description>
Persistence unit for Hibernate User Guide
</description>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>org.hibernate.documentation.userguide.Document</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1" />
<property name="javax.persistence.jdbc.user"
value="sa" />
<property name="javax.persistence.jdbc.password"
value="" />
<property name="hibernate.show_sql"
value="true" />
<property name="hibernate.hbm2ddl.auto"
value="update" />
</properties>
</persistence-unit>
</persistence>
For compliant application-bootstrapping라는 말이 나오는데, 호환되지 않는 경우가 어떤 경우인지 잘 모르겠다.
하여튼 호환되는 경우 javax.persistence.Persistence bootstrap class를 사용하여 EntityManagerFactory를 구축하라고 합니다. 이는 createEntityManagerFactory method를 사용해 구축할 수 있습니다.
// Create an EMF for our CRM persistence-unit.
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "CRM" );
만약 persistence.xml을 제공하지 않고 JPA를 사용하려면 PersistenceUnitInfo를 구현하고 호출하여 모든 구성 옵션을 제공할 수 있다고 한다.
이후 영속성 콘텍스트를 주입하려면 @PersistenceContext 어노테이션을 사용할 수 있습니다.
@PersistenceContext
private EntityManager em;
Configuring the SessionFactory Metadata via the JPA bootstrap
JPA 구현체로 Hibernate를 사용하는 경우 EntityMangerFactory는 SessionFactory의 지원을 받습니다.
그렇기 때문에 우리는 Metadata 객체에 원래는 제공할 수 없는 표준 Hinbernate 구성 설정 정보를 이용하여 다양한 설정을 전달할 수 있습니다.
Hibernate ORM 5.6.14.Final User Guide
Hibernate 버전 별로 부트스트랩 시 EntityManagerFactory의 다양한 설정을 조작하여 커스텀할 수 있는 옵션이 있는 docs 페이지입니다.
public class SqlFunctionMetadataBuilderContributor
implements MetadataBuilderContributor {
@Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"instr", new StandardSQLFunction( "instr", StandardBasicTypes.STRING )
);
}
}
즉 default SessionFactory에서 사용하는 MetadataBuilder 클래스에 접근하여 구성정보를 변경할 수 있으므로 JPA bootstrap 또한 Hibernate Native bootstrap mechanism만큼 유연할 수 있습니다.
'데이터 접근 기술 > JPA' 카테고리의 다른 글
@Converter, Enum (0) | 2023.07.07 |
---|---|
DTO와 Entity (0) | 2022.12.11 |
상속관계 매핑 (0) | 2022.12.04 |
값 타입 (0) | 2022.11.20 |
OSIV (0) | 2022.10.31 |
댓글