본문 바로가기

카테고리 없음

Spring + JPA + Hibernate + Multiple DataSource 설정

개발환경: 
- Spring 3.0.5.RELEASE
- JPA 2.0
- Hibernate 3.6.2.Final
- DBMS: MSSQL 2008 R2(10.50.1600.1)

JPA환경에서 두개이상의 DataSource를 사용하기 위해서 EntityManagerFactory를 두개이상 생성하려고 하면 

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2
...


와 같은 에러가 발생한다.

구글링을 해보면 이 문제에 대한 문의와 토론은 많이 있지만 딱히 해결안이 없어 보인다.
포기하고 iBATIS로 이 문제를 풀려고 했는데 마지막에 시도해본 방법이 성공을 하여 공유하고자 정리한다.

방법을 제공해준 사이트는 아래와 같다.
소스코드: svn checkout http://gwt-spring-jpa-lucene.googlecode.com/svn/com.intre.open.gwtjpa

단, 이때 트랜잭션은 2PC 상황이므로 다중트랜잭션에 대한 문제는 별도 처리를 해줘야 한다.
기회가 되면 다중트랜잰션에 대한 내용도 정리할 생각이다. WAS를 사용하지 않는한 JOTM과 같은 트랜잭션 관리자가 필요하다.

먼저 다음과 같은 Spring applicationContext.xml을 작성한다.

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

  xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"

  xmlns:p="http://www.springframework.org/schema/p"

  xsi:schemaLocation="

  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd

  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

  <!-- context:annotation-config도 등록함 -->

  <context:component-scan base-package="kr.hibid.collector" />

  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

  <bean id="hibidPum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">

    <property name="persistenceXmlLocations">

      <list>

        <value>/test-coll-persistence.xml</value>

        <value>/test-trans-persistence.xml</value>

      </list>

    </property>

    <property name="dataSources">

      <map>

        <entry key="hibidCollDS" value-ref="hibidCollDS" />

        <entry key="hibidTransDS" value-ref="hibidTransDS" />

      </map>

    </property>

    <property name="defaultDataSource" ref="hibidCollDS" />

  </bean>

  <!-- ===================================================================== -->

  <!-- Collection                                                            -->

  <!-- ===================================================================== -->

  <bean id="hibidCollDS" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />

    <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=HibidCollDev" />

    <!-- <property name="connectionProperties" value="charset=eucksc;" /> -->

    <property name="username" value="hibid_coll_dev" />

    <property name="password" value="hibid_coll_dev" />

  </bean>

  <bean id="hibidCollEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="persistenceUnitManager" ref="hibidPum" />

    <property name="persistenceUnitName" value="hibidCollUnit" />

    <property name="loadTimeWeaver">

      <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

    </property>

    <property name="jpaDialect">

      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    </property>

  </bean>

  <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

    <property name="entityManagerFactory" ref="hibidCollEmf" />

    <property name="jpaDialect">

      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    </property>

  </bean>

  <tx:annotation-driven transaction-manager="transactionManager" />

  <!-- ===================================================================== -->

  <!-- Transference                                                          -->

  <!-- ===================================================================== -->

  <bean id="hibidTransDS" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />

    <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=HibidTest" />

    <!-- <property name="connectionProperties" value="charset=eucksc;" /> -->

    <property name="username" value="hibid_test" />

    <property name="password" value="hibid_test" />

  </bean>

  <bean id="hibidTransEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="persistenceUnitManager" ref="hibidPum" />

    <property name="persistenceUnitName" value="hibidTransUnit" />

    <property name="loadTimeWeaver">

      <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

    </property>

    <property name="jpaDialect">

      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    </property>

  </bean>

  <bean id="hibidTransTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">

    <property name="entityManagerFactory" ref="hibidTransEmf" />

    <property name="jpaDialect">

      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    </property>

  </bean>

  <tx:annotation-driven transaction-manager="hibidTransTxManager" />

</beans>


/test-coll-persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

  <persistence-unit name="hibidCollUnit" transaction-type="RESOURCE_LOCAL">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <non-jta-data-source>hibidCollDS</non-jta-data-source>

    <!-- Entity Classes Define Start ======================================= -->

    <class>kr.hibid.collector.domain.notify.PpsBiddingBasisAmount</class>

    <!-- Entity Classes Define End ======================================= -->

    <exclude-unlisted-classes />

    <properties>

      <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />

      <property name="hibernate.archive.autodetection" value="false"/>

      <property name="hibernate.transaction.auto_close_session" value="false"/>

      <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider"/>

      <property name="hibernate.search.default.indexBase" value="lucene-first"/>

      <property name="hibernate.search.default.locking_strategy" value="none"/>

      <property name="hibernate.show_sql" value="true" />

      <property name="hibernate.format_sql" value="true" />

      <property name="hibernate.use_sql_comments" value="false" />

      <property name="hibernate.hbm2ddl.auto" value="update" />

    </properties>

  </persistence-unit>

</persistence>


  /test-trans-persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

  <persistence-unit name="hibidTransUnit" transaction-type="RESOURCE_LOCAL">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <non-jta-data-source>hibidTransDS</non-jta-data-source>

    <!-- Entity Classes Define Start ======================================= -->

    <class>kr.hibid.common.domain.BizType</class>

    <!-- Entity Classes Define End ======================================= -->

    <exclude-unlisted-classes />

    <properties>

      <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />

      <property name="hibernate.archive.autodetection" value="false"/>

      <property name="hibernate.transaction.auto_close_session" value="false"/>

      <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider"/>

      <property name="hibernate.search.default.indexBase" value="lucene-first"/>

      <property name="hibernate.search.default.locking_strategy" value="none"/>

      <property name="hibernate.show_sql" value="true" />

      <property name="hibernate.format_sql" value="true" />

      <property name="hibernate.use_sql_comments" value="false" />

      <property name="hibernate.hbm2ddl.auto" value="update" />

    </properties>

  </persistence-unit>

</persistence>

  

PpsTransServiceTest.java

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = { "/test-collector-context.xml" })

@Transactional(propagation = Propagation.SUPPORTS)

@TransactionConfiguration(defaultRollback = true)

public class PpsTransServiceTest {

    @Autowired

    private PpsCollectionEao collectionEao;

    @Autowired

    private PpsTransEao transEao;

    @Test

    public void testDI() {

        assertNotNull(this.collectionEao);

        assertNotNull(this.transEao);

        List<BizType> bizTypes = this.transEao.findBizTypes();

        assertNotNull(bizTypes);

        List<PpsFacilityBiddingNotify> facilityBiddingNotifies = this.collectionEao.findFacilityBiddingNotifies("20110522120000");

        assertNotNull(facilityBiddingNotifies);

        List<PpsBiddingBasisAmount> biddingBasisAmounts = this.collectionEao.findBiddingBasisAmounts("20110522120000");

        assertNotNull(biddingBasisAmounts);

    }

}

 

PpsCollectionEao.java

@Repository

@Transactional(propagation = Propagation.SUPPORTS)

public class PpsCollectionEao {

    @PersistenceContext(unitName = "hibidCollUnit")

    private EntityManager em;

    public List<PpsFacilityBiddingNotify> findFacilityBiddingNotifies(String baseDateTime) {

        // QL 작성

        StringBuilder qlString = new StringBuilder(200);

        qlString.append("SELECT pfbn FROM PpsFacilityBiddingNotify pfbn ");

        qlString.append("WHERE pfbn.collectDateTime is null ");

        qlString.append("  AND pfbn.transDateTime <= :baseDateTime ");

        // Query 실행 및 결과 반환

        TypedQuery<PpsFacilityBiddingNotify> query = this.em.createQuery(qlString.toString(), PpsFacilityBiddingNotify.class);

        query.setParameter("baseDateTime", baseDateTime);

        List<PpsFacilityBiddingNotify> results = query.getResultList();

        return results;

    }

    public List<PpsBiddingBasisAmount> findBiddingBasisAmounts(String baseDateTime) {

        // QL 작성

        StringBuilder qlString = new StringBuilder(200);

        qlString.append("select pbba from PpsBiddingBasisAmount pbba ");

        qlString.append("WHERE pbba.collectDateTime is null ");

        qlString.append("  AND pbba.transDateTime <= :baseDateTime ");

        // Query 실행 및 결과 반환

        TypedQuery<PpsBiddingBasisAmount> query = this.em.createQuery(qlString.toString(), PpsBiddingBasisAmount.class);

        query.setParameter("baseDateTime", baseDateTime);

        List<PpsBiddingBasisAmount> results = query.getResultList();

        return results;

    }

}

 

PpsTransEao.java

@Repository

@Transactional(value = "hibidTransTxManager", propagation = Propagation.SUPPORTS)

public class PpsTransEao {

    @PersistenceContext(unitName = "hibidTransUnit")

    private EntityManager em;

    public List<BizType> findBizTypes() {

        List<BizType> results = this.em.createQuery("select bt from BizType bt", BizType.class).setMaxResults(10).getResultList();

        return results;

    }

}