【问题标题】:How to dynamic connect database using Spring 3.2 and JPA 2.0如何使用 Spring 3.2 和 JPA 2.0 动态连接数据库
【发布时间】:2013-07-24 07:27:52
【问题描述】:

我正在使用 Spring 3.2.3 和 JPA 2.0。我想基于枚举动态连接我的数据库。我有这样的配置

<bean id="legacyDataSource" name="myLegacyDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
    <property name="driverClassName" value="${jdbc.legacy.driverClassName}" />
    <property name="url" value="${jdbc.legacy.url}" />  
    <property name="username" value="${jdbc.legacy.username}" />
    <property name="password" value="${jdbc.legacy.password}" />
</bean>

 <bean id="modernDataSource" name="myModernDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
    <property name="driverClassName" value="${jdbc.modern.driverClassName}" />
    <property name="url" value="${jdbc.modern.url}" />  
    <property name="username" value="${jdbc.modern.username}" />
    <property name="password" value="${jdbc.modern.password}" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf"/>
</bean>

<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:database/jdbc.properties" />
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="order" value="1" />
</bean>

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

<bean id="emf" name="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="vendorAdaptor" />         
    <property name="packagesToScan" value="com.softech.ls360.integration.regulators.plcb.domain"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
            <prop key="hibernate.max_fetch_depth">3</prop>
            <prop key="hibernate.jdbc.fetch_size">50</prop>
            <prop key="hibernate.jdbc.batch_size">10</prop>
            <prop key="hibernate.show_sql">true</prop>              
        </props>        
    </property>
</bean>   

我将枚举声明为

public enum DatabaseType {

    LEGACY,
    MODERN

} //end of enum DatabaseType

现在我想在我的主类的基础上枚举数据库开关。为此,我认为我需要更改

<property name="dataSource" ref="dataSource" />

在运行时&lt;bean id="emf" name="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true"&gt;。我说的对吗?

有没有办法做到这一点,或者 spring 是否提供任何其他方式来在运行时切换数据库?

我找到了这篇文章Dynamic DataSource Routing,但这是一篇很老的文章,而且在这篇文章中也没有使用 JPA。请帮忙

谢谢。

编辑 --------------------------------

@Lazy
@Service("dbManager") 
@Repository
@Transactional
public class DatabaseManager {

    public enum EnumLmsPlateform {      
        MODERN,
        LEGACY;
    }

    private EnumLmsPlateform lmsPlatform;

    public EnumLmsPlateform getLmsPlatform() {
        return lmsPlatform;
    }

    public void setLmsPlatform(EnumLmsPlateform lmsPlatform) {
        this.lmsPlatform = lmsPlatform;
    }

    @PostConstruct
    public void init() {    
        setLmsPlatform(EnumLmsPlateform.MODERN);    
    }

    @PersistenceContext(unitName="legacy_emf")
    private EntityManager legacyEm;

    @PersistenceContext(unitName="modern_emf")
    private EntityManager ls360Em;

    @SuppressWarnings("unchecked")
    @Transactional(readOnly=true)
    public List<Object> getResultList(String query, Class<?> mappingClass) throws Exception {

        EntityManager em = null;

        if (lmsPlatform == EnumLmsPlateform.LEGACY) {
            em = legacyEm;  
        } else if (lmsPlatform == EnumLmsPlateform.MODERN){ 
            em = ls360Em;   
        }
        Query emQuery = em.createNativeQuery(query, mappingClass);  
        return  emQuery.getResultList();

    } //end of findTraineeFromLegacy()
} //end of class

这是我的 spring 文件

legacy-spring.xml

<bean id="legacyDataSource" name="legacy_DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
    <property name="driverClassName" value="${jdbc.legacy.driverClassName}" />
    <property name="url" value="${jdbc.legacy.url}" />  
    <property name="username" value="${jdbc.legacy.username}" />
    <property name="password" value="${jdbc.legacy.password}" />
</bean>

<bean id="legacyTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" lazy-init="true">
    <property name="entityManagerFactory" ref="legacyEmf"/>
</bean>

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

<bean id="legacyEmf" name="legacy_emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true" >
    <property name="dataSource" ref="legacyDataSource" />
    <property name="jpaVendorAdapter" ref="vendorAdaptor" />         
    <property name="packagesToScan" value="com.softech.ls360.integration.regulators.plcb.domain"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
            <prop key="hibernate.max_fetch_depth">3</prop>
            <prop key="hibernate.jdbc.fetch_size">50</prop>
            <prop key="hibernate.jdbc.batch_size">10</prop>
            <prop key="hibernate.show_sql">true</prop>              
        </props>        
    </property>
</bean> 

moderns-spring.xml 的文件相同

然后我将这两个文件都包含在我的 maim spring 中

app-context.xml

<import resource="classpath:database/hibernate-context.xml"/>
<import resource="classpath:database/legacyJpa-context.xml"/>
<import resource="classpath:database/modernJpa-context.xml"/> 

<context:annotation-config/>
....

但是在这个设计中有一个流程。两位经理都包括在内。传统与现代。我的意思是当我的 dbManager bean 被初始化时,它同时需要@PersistenceContext(unitName="legacy_emf"), @PersistenceContext(unitName="modern_emf")。应该发生什么,因为我的默认 persistence context 是现代的,所以只有 @PersistenceContext(unitName="modern_emf") 这个持久性上下文应该在 bean 初始化时可用。然后如果我设置我的enum to legacy 然后unitName="legacy_emf" 这应该是可用的。你找到我了吗?

谢谢

编辑 2: -------------------------------------------------- -------------------

我在下面更改了我的 dbManager 类

@Lazy
@Service("dbManager") 
@Repository
@Transactional
public class DatabaseManager {

    public enum EnumLmsPlateform {
        MODERN,
        LEGACY;
    }

    private EnumLmsPlateform lmsPlatform;

    public DatabaseManager() {

    }

    @PostConstruct
    public void init() {
        setLmsPlatform(EnumLmsPlateform.MODERN);
    }


    public EnumLmsPlateform getLmsPlatform() {
        return lmsPlatform;
    }

    public void setLmsPlatform(EnumLmsPlateform lmsPlatform) {
        this.lmsPlatform = lmsPlatform;
    }

    @ModernTx
    public List<Object> getModernResultSet(String query, Class<?> mappingClass) { 
        EntityManager modernEm = getEntityManagerFormBoundTransactionalThread();
        return getResultSet(modernEm, query, mappingClass);

    }

    @LegacyTx 
    public List<Object> getLegacyResultSet(String query, Class<?> mappingClass) { 
        EntityManager legacyEm = getEntityManagerFormBoundTransactionalThread();
        return getResultSet(legacyEm, query, mappingClass);
    } 

    @Transactional 
    public List<Object> getDefaultResultSet(String query, Class<?> mappingClass) { 
        EntityManager defaultEm = getEntityManagerFormBoundTransactionalThread();
        return getResultSet(defaultEm, query, mappingClass);
    }

    private List<Object> getResultSet(EntityManager em, String query, Class<?> mappingClass) {
        Query emQuery = em.createNativeQuery(query, mappingClass);  
        return  emQuery.getResultList();    
    }

    private EntityManager getEntityManagerFormBoundTransactionalThread(){
        Map<Object, Object>  map =   TransactionSynchronizationManager.getResourceMap();   
        for (Object value  :map.values()) {
            if(value instanceof EntityManagerHolder){       
                return ((EntityManagerHolder)value).getEntityManager();

            }  
        } 

        return null;  

    } //end of  getEntityManagerFormBoundTransactionalThread()  

} //end of class

我的测试课

public class TestDynamicJpa {

    String springXmlFile = "classpath:spring/app-context-xml.xml";

    GenericXmlApplicationContext springContext = SpringUtil.loadSpringContext(springXmlFile);

    List<Object> traineeList = getLegacyResultList(springContext);

    private List<Object> getLegacyResultList(GenericXmlApplicationContext springContext) throws Exception {

        DatabaseManager dbManager = springContext.getBean("dbManager", DatabaseManager.class);

        List<Object> resultList = dbManager.getLegacyResultSet(FIND_TRAINEE_LEGACY, LegacyTrainee.class);

        return resultList;

    } //end of getLegacyResultList()

} //end of class TestDynamicJpa

一个问题。当我这样做时

List<Object> resultList = dbManager.getLegacyResultSet(FIND_TRAINEE_LEGACY, LegacyTrainee.class);

然后它从 Map 中获取 LegacyTransactionManager,很好。但是如果我这样做了

List<Object> resultList = dbManager.getDefaultResultSet(FIND_TRAINEE_LEGACY, LegacyTrainee.class);

然后它从 Map 获取哪个事务管理器。 LegacyModern ?

我也用这个配置检查了它

 <import resource="classpath:database/hibernate-context.xml"/>
 <import resource="classpath:database/legacyJpa-context.xml"/>
 <!--  
 <import resource="classpath:database/ls360Jpa-context.xml"/> 
 -->

即,只导入一个 Jpa 上下文,它工作正常 :)。在我之前的@PersistenceUnit 配置中,如果我注释掉一个文件,我会收到错误:)。

谢谢:)

【问题讨论】:

    标签: spring spring-data-jpa spring-jdbc


    【解决方案1】:

    在这种情况下,由于您还有 2 个数据源,您应该再创建 2 个不同名称的 transactionManager。

    <bean id="legacyTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emfLegacy"/>
    </bean>
    
    
    <bean id="modernTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emfModern"/>
    </bean>
    
    <bean id="emfLegacy" name="myEmfLegacy" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true">
        <property name="dataSource" ref="legacyDataSource" />
        <property name="jpaVendorAdapter" ref="vendorAdaptor" />         
        <property name="packagesToScan" value="com.softech.ls360.integration.regulators.plcb.domain"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.show_sql">true</prop>              
            </props>        
        </property>
    </bean>   
    
    
    
    <bean id="emfModern" name="myEmfModern" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true">
        <property name="dataSource" ref="modernDataSource" />
        <property name="jpaVendorAdapter" ref="vendorAdaptor" />         
        <property name="packagesToScan" value="com.softech.ls360.integration.regulators.plcb.domain"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.show_sql">true</prop>              
            </props>        
        </property>
    </bean>   
    
    
    
     @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Transactional("modernTransactionManager")
    public @interface ModernTx {
    }
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Transactional("legacyTransactionManager")
    public @interface LegacyTx {
    }  
    

    之后剩下的就很简单了,只需使用@ModernTx 或@LegacyTx 注释你的bean 的db 方法,如下所示。

    public class TransactionalService {
    
        @ModernTx
        public void setSomething(String name) { 
    
             EntityManager currentEm = getEntityManagerFormBoundTransactionalThread();
             /*You can do anything with this Modern em*/
        }
    
        @LegacyTx 
        public void doSomething() { 
    
             EntityManager currentEm = getEntityManagerFormBoundTransactionalThread();
             /*You can do anything with this Legacy em*/
    
        } 
    
    
        @Transactional 
        public void buySomething() { 
    
             EntityManager currentEm = getEntityManagerFormBoundTransactionalThread();
             /*You can do anything with this default em*/
    
        }         
    
    
    
    
       private getEntityManagerFormBoundTransactionalThread(){
    
            Map<Object, Object>  map =                    TransactionSynchronizationManager.getResourceMap();
            for (Object value  :map.values()) {
                if(value instanceof EntityManagerHolder){
                                return ((EntityManagerHolder)value).getEntityManager();
                    }
                }
                }     
         }
    

    但是,当您希望使用 dafult 数据源时,只需使用 @Transactional,因为您已在上下文中配置了默认事务管理器,如下所示。

    【讨论】:

    • 您好,谢谢。请检查我的编辑。我已经完全按照你说的做了。你能解释一下@ModernTx 和`@LegacyTx` 的注释是什么吗?他们是否基于枚举切换?
    • 谢谢。请检查我的编辑 2 :)
    • 您声明,如果您只使用@transactional,您将使用此事务管理器以上在您的 XML 中。而 myemf 使用 "datasource" 。您在某个地方配置了它,但我在这里看不到它。
    • 可能“数据源”bean id 在“classpath:database/hibernate-context.xml”中
    • 是的,数据源 bean id 位于 hibernate-context.xml。实际上我这样做是因为如果我想使用另一个持久性提供程序供应商,那么我只需创建另一个文件,如 eclipselink-context.xml 并提供他们的参考。实际上,&lt;tx:annotation-driven transaction-manager="transactionManager"/&gt; 是旧的。 Edit 1 包含新的配置文件 legacy-spring.xml 相同的文件用于 mpdern-spring.xml 仅指向现代数据库......无论如何,但你解决了我的问题。只有最后一个问题是which transaction manager it gets from Map. Legacy or Modern ?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    • 2016-12-12
    • 1970-01-01
    • 2017-08-10
    • 2014-03-14
    相关资源
    最近更新 更多