역시 제 나름대로 테스트 환경부터 만들어서 공부하는 습관이 도움이 되네요...
그래서 제가 만든 환경에 대해 설명하고, 이 것을 바탕으로 hibernate-ogm을 제품에 쉽게 적용할 수 있도록 해 보겠습니다.
hibernate-ogm 도 hibernate 와 유사하게 환경설정을 합니다. 다만, hibernate 3.x 대의 Configuration을 이용하여 SessionFactory를 build 하는 것과 유사하게 SessionFactory를 빌드합니다. 즉 hibernate 4.x대와는 다르게 설정하네요.
또 한가지 확실하지는 않지만, Configuration에서 Package 추가는 잘 안되고, annotationClass 는 제대로 되는군요...
=> 제가 잘못알고 알고 있었네요. package 추가 시에는 scan을 해줘야 하는데 그것은 새로 구현을 해야 하는 거더군요^^
우선 모든 Datastore (NoSql이라 그런지 Database 라 하지 않고 Datastore 라고 하는군요) 에 공통되는 부분을 구현한 GridDatastoreConfigBase.java 파일을 보면
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package kr.debop4j.ogm.spring.cfg; | |
import kr.debop4j.data.hibernate.interceptor.StatefulEntityInterceptor; | |
import kr.debop4j.data.hibernate.repository.HibernateRepositoryFactory; | |
import kr.debop4j.data.hibernate.unitofwork.UnitOfWorkFactory; | |
import lombok.extern.slf4j.Slf4j; | |
import org.hibernate.SessionFactory; | |
import org.hibernate.cfg.Environment; | |
import org.hibernate.engine.spi.SessionFactoryImplementor; | |
import org.hibernate.ogm.cfg.OgmConfiguration; | |
import org.hibernate.ogm.datastore.impl.DatastoreServices; | |
import org.hibernate.ogm.datastore.spi.DatastoreProvider; | |
import org.hibernate.ogm.dialect.GridDialect; | |
import org.hibernate.service.Service; | |
import org.hibernate.service.spi.ServiceRegistryImplementor; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import java.util.Properties; | |
/** | |
* hibernate-ogm 의 환경설정을 Spring Configuration으로 구현합니다. | |
* | |
* @author sunghyouk.bae@gmail.com | |
* 13. 3. 23. 오후 2:22 | |
*/ | |
@Configuration | |
@Slf4j | |
public abstract class GridDatastoreConfigBase { | |
/** | |
* DataStoreProvider 를 제공합니다. | |
*/ | |
@Bean | |
public DatastoreProvider datastoreProvider() { | |
return (DatastoreProvider) getService(DatastoreProvider.class); | |
} | |
/** | |
* {@link GridDialect} 를 제공합니다. | |
*/ | |
@Bean | |
public GridDialect gridDialect() { | |
return ((DatastoreServices) getService(DatastoreServices.class)).getGridDialect(); | |
} | |
/** | |
* Hibernate SessionFactory 를 제공합니다. | |
*/ | |
@Bean | |
public SessionFactory sessionFactory() { | |
if (log.isInfoEnabled()) | |
log.info("hiberante-ogm 용 SessionFactory를 생성합니다..."); | |
OgmConfiguration cfg = new OgmConfiguration(); | |
for (String pkgName : getMappedPackageNames()) { | |
cfg.addPackage(pkgName); | |
} | |
for (Class annoatatedClass : getMappedEntities()) { | |
cfg.addAnnotatedClass(annoatatedClass); | |
} | |
cfg.setInterceptor(hibernateInterceptor()); | |
cfg.setProperties(getHibernateOgmProperties()); | |
if (log.isInfoEnabled()) | |
log.info("hiberante-ogm 용 SessionFactory를 생성했습니다!!!"); | |
return cfg.buildSessionFactory(); | |
} | |
@Bean | |
public org.hibernate.Interceptor hibernateInterceptor() { | |
return new StatefulEntityInterceptor(); | |
} | |
@Bean | |
public UnitOfWorkFactory unitOfWorkFactory() { | |
if (log.isInfoEnabled()) | |
log.info("UnitOfWorkFactory를 생성합니다..."); | |
UnitOfWorkFactory factory = new UnitOfWorkFactory(); | |
factory.setSessionFactory(sessionFactory()); | |
return factory; | |
} | |
@Bean | |
public HibernateRepositoryFactory hibernateRepositoryFactory() { | |
return new HibernateRepositoryFactory(); | |
} | |
protected String getDatabaseName() { | |
return "debop4j_ogm_test"; | |
} | |
protected String[] getMappedPackageNames() { | |
return new String[0]; | |
} | |
protected Class[] getMappedEntities() { | |
return new Class[0]; | |
} | |
protected Properties getHibernateProperties() { | |
Properties props = new Properties(); | |
props.setProperty(Environment.USE_NEW_ID_GENERATOR_MAPPINGS, "true"); | |
props.setProperty(Environment.HBM2DDL_AUTO, "none"); | |
return props; | |
} | |
protected Properties getHibernateOgmProperties() { | |
return getHibernateProperties(); | |
} | |
protected org.hibernate.service.Service getService(Class<? extends Service> serviceImpl) { | |
SessionFactoryImplementor sessionFactory = sfi(); | |
ServiceRegistryImplementor serviceRegistry = sessionFactory.getServiceRegistry(); | |
return serviceRegistry.getService(serviceImpl); | |
} | |
protected SessionFactoryImplementor sfi() { | |
return (SessionFactoryImplementor) sessionFactory(); | |
} | |
} |
public SessionFactory sessionFactory() {...} 는 아주 익숙한 코드지요? 단지 Configuration class가 hibernate 것이 아닌 hibernate-ogm의 OgmConfiguration을 사용한다는 것만 다릅니다.
아래의 getDatabaseName(), getMappedPackageNames(), getMappedEntities() 메소드는 각자 Datastore와 제품에 따라 재정의하면 됩니다.
그 밑에 getHibernateProperties() 와 getHibernateOgmProperties() 는 각각 Hibernate 설정과 Hibernate-Ogm 설정을 추가할 수 있습니다. 이 부분도 재정의를 통해 추가하시면 됩니다.
그럼 MongoDB를 사용하는 환경설정은? 위의 GridDataStoreConfigBase를 상속하여 몇가지 메소드를 재정의만 하면 됩니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package kr.debop4j.ogm.spring.cfg.mongodb; | |
import kr.debop4j.ogm.spring.cfg.GridDatastoreConfigBase; | |
import lombok.extern.slf4j.Slf4j; | |
import org.hibernate.ogm.datastore.mongodb.AssociationStorage; | |
import org.hibernate.ogm.datastore.mongodb.Environment; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import java.util.Properties; | |
/** | |
* MongoDB를 Datastore로 사용하는 hibernate-ogm 에 대한 환경설정입니다. | |
* | |
* @author sunghyouk.bae@gmail.com | |
* 13. 3. 23. 오후 3:53 | |
*/ | |
@Configuration | |
@Slf4j | |
public abstract class MongoDBConfigBase extends GridDatastoreConfigBase { | |
public static final String MONGODB_DATASTORE_PROVIDER = "org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider"; | |
protected Properties getHibernateOgmProperties() { | |
Properties props = getHibernateProperties(); | |
props.setProperty("hibernate.ogm.datastore.provider", MONGODB_DATASTORE_PROVIDER); | |
props.put(Environment.MONGODB_DATABASE, getDatabaseName()); | |
props.put(Environment.MONGODB_TIMEOUT, 200); | |
props.put(Environment.MONGODB_ASSOCIATIONS_STORE, getAssociationStorage().name()); | |
return props; | |
} | |
protected AssociationStorage getAssociationStorage() { | |
return AssociationStorage.COLLECTION; | |
} | |
@Bean | |
public javax.transaction.TransactionManager transactionManager() { | |
return com.arjuna.ats.jta.TransactionManager.transactionManager(); | |
} | |
} |
MongoDB 만의 설정을 보면 database 명을 설정해줘야 하고, 엔티티의 저장 방식을 설정해주게
됩니다. AssociationStorage enum 값의 설명을 보면 ...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Hibernate, Relational Persistence for Idiomatic Java | |
* | |
* JBoss, Home of Professional Open Source | |
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors | |
* as indicated by the @authors tag. All rights reserved. | |
* See the copyright.txt in the distribution for a | |
* full listing of individual contributors. | |
* | |
* This copyrighted material is made available to anyone wishing to use, | |
* modify, copy, or redistribute it subject to the terms and conditions | |
* of the GNU Lesser General Public License, v. 2.1. | |
* This program is distributed in the hope that it will be useful, but WITHOUT A | |
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | |
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. | |
* You should have received a copy of the GNU Lesser General Public License, | |
* v.2.1 along with this distribution; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
* MA 02110-1301, USA. | |
*/ | |
package org.hibernate.ogm.datastore.mongodb; | |
/** | |
* Defines the various association storage strategies | |
* | |
* @author Alan Fitton <alan at eth0.org.uk> | |
* @author Emmanuel Bernard <emmanuel@hibernate.org> | |
*/ | |
public enum AssociationStorage { | |
/** | |
* Store the association info in a unique MongoDB collection for all associations | |
*/ | |
GLOBAL_COLLECTION, | |
/** | |
* Store the association in a dedicated MongoDB collection per association | |
*/ | |
COLLECTION, | |
/** | |
* Store association information from within the entity | |
*/ | |
IN_ENTITY | |
} |
입니다. Entity 들을 전역 컬렉션에 저장, 지정한 컬렉션에 저장, 엔티티별로 저장과 같이 3가지 방식이 있습니다.
뭐 Lazy Initialization을 많이 사용하려면 엔티티별로 저장하고, 일반적으로는 컬렉션에 저장하는게 좋으리라 생각됩니다.
자 그럼 실제 테스트 시에 사용할 Configuration을 보면은
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.hibernate.ogm.test.mongodb; | |
import kr.debop4j.ogm.spring.cfg.mongodb.MongoDBConfigBase; | |
import lombok.extern.slf4j.Slf4j; | |
import org.hibernate.ogm.test.mongodb.model.Module; | |
import org.hibernate.ogm.test.mongodb.model.Project; | |
import org.springframework.context.annotation.Configuration; | |
/** | |
* MongoDB 를 DataStore로 사용하는 Configuration | |
* | |
* @author sunghyouk.bae@gmail.com | |
* 13. 3. 23. 오후 2:31 | |
*/ | |
@Configuration | |
@Slf4j | |
public class MongoDBConfiguration extends MongoDBConfigBase { | |
@Override | |
protected String getDatabaseName() { | |
return "debop4j_ogm_test"; | |
} | |
@Override | |
protected String[] getMappedPackageNames() { | |
return new String[]{ | |
Project.class.getPackage().getName(), | |
}; | |
} | |
@Override | |
protected Class[] getMappedEntities() { | |
return new Class[]{ | |
Module.class, | |
Project.class | |
}; | |
} | |
} |
과 같습니다. DB명을 지정하고, 매핑된 엔티티를 지정해 주는 것으로 끝납니다^^
이제부터는 위의 Configuration을 이용하여 테스트 코드를 작성하여 테스트를 수행하면됩니다
hibernate-ogm은 현재까지 NoSQL을 단순 저장소로 밖에 활용 못한 것을 Object Grid 방식으로 사용할 수 있도록 한 차원 업그래이드된 방법을 제공합니다.
이를 통해 앞으로는 RDBMS 뿐 아니라 NoSQL도 특정 제품에 구애받지 않고 사용할 수 있었으면 합니다.
댓글 없음:
댓글 쓰기