<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    京山游俠

    專注技術,拒絕扯淡
    posts - 50, comments - 868, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理
    在SpringSide 3 中,白衣提供的預先配置好的環境非常有利于用戶進行快速開發,但是同時也會為擴展帶來一些困難。最直接的例子就是關于在項目中使用多個數據源的問題,似乎很難搞。在上一篇中,我探討了SpringSide 3 中的數據訪問層,在這一篇中,我立志要解決多數據源配置的難題,我的思路是這樣的:

    第一步、測試能否配置多個DataSource
    第二步、測試能否配置多個SessionFactory
    第三步、測試能否配置多個TransactionManager
    第四步、測試能否使用多個TransactionManager,也就是看能否配置多個<tx:annotation-driven/>

    基本上到第四步就應該走不通了,因為Spring中似乎不能配置多個<tx:annotation-driven/>,而且@transactional注解也無法讓用戶選擇具體使用哪個TransactionManager。也就是說,在SpringSide的應用中,不能讓不同的數據源分別屬于不同的事務管理器,多數據源只能使用分布式事務管理器,那么測試思路繼續如下進行:

    第五步、測試能否配置JTATransactionManager

    如果到這一步,項目還能順利在Tomcat中運行的話,我們就算大功告成了。但我總認為事情不會那么順利,我總覺得JTATransactionManager需要應用服務器的支持,而且需要和JNDI配合使用,具體是不是這樣,那只有等測試后才知道。如果被我不幸言中,那么進行下一步:

    第六步、更換Tomcat為GlassFish,更換JDBC的DataSource為JNDI查找的DataSource,然后配置JTATransactionManager

    下面測試開始,先假設場景,還是繼續用上一篇中提到的簡單的文章發布系統,假設該系統運行一段時間后非常火爆,單靠一臺服務器已經無法支持巨大的用戶數,這時候,站長想到了把數據進行水平劃分,于是,需要建立一個索引數據庫,該索引數據庫需保存每一篇文章的Subject及其內容所在的Web服務器,而每一個Web服務器上運行的項目,需要同時訪問索引數據庫和內容數據庫。所以,需要創建索引數據庫,如下:
    create database puretext_index;

    use puretext_index;

    create table articles(
    id 
    int primary key auto_increment,
    subject 
    varchar(256),
    webserver 
    varchar(30)
    );

    第一步測試,配置多個DataSource,配置文件如下:
    application.properties:
    jdbc.urlContent=jdbc:mysql://localhost:3306/PureText?useUnicode=true&characterEncoding=utf8
    jdbc.urlIndex
    =jdbc:mysql://localhost:3306/PureText_Index?useUnicode=true&characterEncoding=utf8

    applicationContext.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jee
    ="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context
    ="http://www.springframework.org/schema/context"
        xsi:schemaLocation
    ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
        default-lazy-init
    ="true">

        
    <description>Spring公共配置文件 </description>

        
    <!-- 定義受環境影響易變的變量 -->
        
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
            
    <property name="ignoreResourceNotFound" value="true" />
            
    <property name="locations">
                
    <list>
                    
    <!-- 標準配置 -->
                    
    <value>classpath*:/application.properties</value>
                    
    <!-- 本地開發環境配置 -->
                    
    <value>classpath*:/application.local.properties</value>
                    
    <!-- 服務器生產環境配置 -->
                    
    <!-- <value>file:/var/myapp/application.server.properties</value> -->
                
    </list>
            
    </property>
        
    </bean>

        
    <!-- 使用annotation 自動注冊bean,并保證@Required,@Autowired的屬性被注入 -->
        
    <context:component-scan base-package="cn.puretext" />

        
    <!-- 數據源配置,使用應用內的DBCP數據庫連接池 -->
        
    <bean id="dataSourceContent" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            
    <!-- Connection Info -->
            
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            
    <property name="url" value="${jdbc.urlContent}" />
            
    <property name="username" value="${jdbc.username}" />
            
    <property name="password" value="${jdbc.password}" />

            
    <!-- Connection Pooling Info -->
            
    <property name="initialSize" value="5" />
            
    <property name="maxActive" value="100" />
            
    <property name="maxIdle" value="30" />
            
    <property name="maxWait" value="1000" />
            
    <property name="poolPreparedStatements" value="true" />
            
    <property name="defaultAutoCommit" value="false" />
        
    </bean>
        
    <bean id="dataSourceIndex" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            
    <!-- Connection Info -->
            
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            
    <property name="url" value="${jdbc.urlIndex}" />
            
    <property name="username" value="${jdbc.username}" />
            
    <property name="password" value="${jdbc.password}" />

            
    <!-- Connection Pooling Info -->
            
    <property name="initialSize" value="5" />
            
    <property name="maxActive" value="100" />
            
    <property name="maxIdle" value="30" />
            
    <property name="maxWait" value="1000" />
            
    <property name="poolPreparedStatements" value="true" />
            
    <property name="defaultAutoCommit" value="false" />
        
    </bean>

        
    <!-- 數據源配置,使用應用服務器的數據庫連接池 -->
        
    <!--<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/ExampleDB" />-->

        
    <!-- Hibernate配置 -->
        
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceContent" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>

        
    <!-- 事務管理器配置,單數據源事務 -->
        
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            
    <property name="sessionFactory" ref="sessionFactory" />
        
    </bean>

        
    <!-- 事務管理器配置,多數據源JTA事務-->
        
    <!--
            <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager or
            WebLogicJtaTransactionManager" />
        
    -->

        
    <!-- 使用annotation定義事務 -->
        
    <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>

    這個時候運行上一篇文章中寫好的單元測試DaoTest.java,結果發現還是會出錯,錯誤原因如下:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.puretext.unit.service.DaoTest': Autowiring of methods failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests.setDataSource(javax.sql.DataSource); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: [dataSourceContent, dataSourceIndex]

    經過分析,發現是測試類的基類需要注入DataSource,而現在配置了多個DataSource,所以Spring不知道哪個DataSource匹配了,所以需要改寫DaoTest.java,如下:
    package cn.puretext.unit.service;

    import java.util.List;

    import javax.annotation.Resource;
    import javax.sql.DataSource;

    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springside.modules.orm.Page;
    import org.springside.modules.test.junit4.SpringTxTestCase;

    import cn.puretext.dao.ArticleDao;
    import cn.puretext.entity.web.Article;

    public class DaoTest extends SpringTxTestCase {
        @Autowired
        
    private ArticleDao articleDao;
        
        
    public ArticleDao getArticleDao() {
            
    return articleDao;
        }

        
    public void setArticleDao(ArticleDao articleDao) {
            
    this.articleDao = articleDao;
        }

        @Override
        @Resource(name 
    = "dataSourceContent")
        
    public void setDataSource(DataSource dataSource) {
            
    // TODO Auto-generated method stub
            super.setDataSource(dataSource);
        }

        @Test
        
    public void addArticle() {
            Article article 
    = new Article();
            article.setSubject(
    "article test");
            article.setContent(
    "article test");
            articleDao.save(article);
        }
        
        @Test
        
    public void pageQuery() {
            Page
    <Article> page = new Page<Article>();
            page.setPageSize(
    10);
            page.setPageNo(
    2);
            page 
    = articleDao.getAll(page);
            List
    <Article> articles = page.getResult();
        }
    }

     改變的內容主要為重寫了基類中的setDataSource方法,并使用@Resource注解指定使用的DataSource為dataSourceContent。經過修改后,單元測試成功運行。

    第二步,配置多個SessionFactory,配置文件如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jee
    ="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context
    ="http://www.springframework.org/schema/context"
        xsi:schemaLocation
    ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
        default-lazy-init
    ="true">

        
    <description>Spring公共配置文件 </description>

        
    <!-- 定義受環境影響易變的變量 -->
        
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
            
    <property name="ignoreResourceNotFound" value="true" />
            
    <property name="locations">
                
    <list>
                    
    <!-- 標準配置 -->
                    
    <value>classpath*:/application.properties</value>
                    
    <!-- 本地開發環境配置 -->
                    
    <value>classpath*:/application.local.properties</value>
                    
    <!-- 服務器生產環境配置 -->
                    
    <!-- <value>file:/var/myapp/application.server.properties</value> -->
                
    </list>
            
    </property>
        
    </bean>

        
    <!-- 使用annotation 自動注冊bean,并保證@Required,@Autowired的屬性被注入 -->
        
    <context:component-scan base-package="cn.puretext" />

        
    <!-- 數據源配置,使用應用內的DBCP數據庫連接池 -->
        
    <bean id="dataSourceContent" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            
    <!-- Connection Info -->
            
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            
    <property name="url" value="${jdbc.urlContent}" />
            
    <property name="username" value="${jdbc.username}" />
            
    <property name="password" value="${jdbc.password}" />

            
    <!-- Connection Pooling Info -->
            
    <property name="initialSize" value="5" />
            
    <property name="maxActive" value="100" />
            
    <property name="maxIdle" value="30" />
            
    <property name="maxWait" value="1000" />
            
    <property name="poolPreparedStatements" value="true" />
            
    <property name="defaultAutoCommit" value="false" />
        
    </bean>
        
    <bean id="dataSourceIndex" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            
    <!-- Connection Info -->
            
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            
    <property name="url" value="${jdbc.urlIndex}" />
            
    <property name="username" value="${jdbc.username}" />
            
    <property name="password" value="${jdbc.password}" />

            
    <!-- Connection Pooling Info -->
            
    <property name="initialSize" value="5" />
            
    <property name="maxActive" value="100" />
            
    <property name="maxIdle" value="30" />
            
    <property name="maxWait" value="1000" />
            
    <property name="poolPreparedStatements" value="true" />
            
    <property name="defaultAutoCommit" value="false" />
        
    </bean>

        
    <!-- 數據源配置,使用應用服務器的數據庫連接池 -->
        
    <!--<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/ExampleDB" />-->

        
    <!-- Hibernate配置 -->
        
    <bean id="sessionFactoryContent" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceContent" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>
        
    <bean id="sessionFactoryIndex" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceIndex" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>

        
    <!-- 事務管理器配置,單數據源事務 -->
        
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            
    <property name="sessionFactory" ref="sessionFactoryContent" />
        
    </bean>

        
    <!-- 事務管理器配置,多數據源JTA事務-->
        
    <!--
            <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager or
            WebLogicJtaTransactionManager" />
        
    -->

        
    <!-- 使用annotation定義事務 -->
        
    <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>

    運行單元測試,報錯,錯誤代碼如下:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.puretext.unit.service.DaoTest': Autowiring of fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private cn.puretext.dao.ArticleDao cn.puretext.unit.service.DaoTest.articleDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'articleDao': Autowiring of methods failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springside.modules.orm.hibernate.SimpleHibernateDao.setSessionFactory(org.hibernate.SessionFactory); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: [sessionFactoryContent, sessionFactoryIndex]

    這和上面出現的錯誤是異曲同工的,只不過這次是ArticleDao類里面不知道注入哪一個SessionFactory,因此,需要修改ArticleDao類,重寫setSessionFactory方法,并用@Resource注解指定,如下:
    package cn.puretext.dao;


    import javax.annotation.Resource;

    import org.hibernate.SessionFactory;
    import org.springframework.stereotype.Repository;
    import org.springside.modules.orm.hibernate.HibernateDao;

    import cn.puretext.entity.web.Article;

    @Repository
    public class ArticleDao extends HibernateDao<Article, Long> {

        @Override
        @Resource(name 
    = "sessionFactoryContent")
        
    public void setSessionFactory(SessionFactory sessionFactory) {
            
    // TODO Auto-generated method stub
            super.setSessionFactory(sessionFactory);
        }

    }

    運行單元測試,成功。

    第三步、配置多個TransactionManager,如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jee
    ="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context
    ="http://www.springframework.org/schema/context"
        xsi:schemaLocation
    ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
        default-lazy-init
    ="true">

        
    <description>Spring公共配置文件 </description>

        
    <!-- 定義受環境影響易變的變量 -->
        
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
            
    <property name="ignoreResourceNotFound" value="true" />
            
    <property name="locations">
                
    <list>
                    
    <!-- 標準配置 -->
                    
    <value>classpath*:/application.properties</value>
                    
    <!-- 本地開發環境配置 -->
                    
    <value>classpath*:/application.local.properties</value>
                    
    <!-- 服務器生產環境配置 -->
                    
    <!-- <value>file:/var/myapp/application.server.properties</value> -->
                
    </list>
            
    </property>
        
    </bean>

        
    <!-- 使用annotation 自動注冊bean,并保證@Required,@Autowired的屬性被注入 -->
        
    <context:component-scan base-package="cn.puretext" />

        
    <!-- 數據源配置,使用應用內的DBCP數據庫連接池 -->
        
    <bean id="dataSourceContent" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            
    <!-- Connection Info -->
            
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            
    <property name="url" value="${jdbc.urlContent}" />
            
    <property name="username" value="${jdbc.username}" />
            
    <property name="password" value="${jdbc.password}" />

            
    <!-- Connection Pooling Info -->
            
    <property name="initialSize" value="5" />
            
    <property name="maxActive" value="100" />
            
    <property name="maxIdle" value="30" />
            
    <property name="maxWait" value="1000" />
            
    <property name="poolPreparedStatements" value="true" />
            
    <property name="defaultAutoCommit" value="false" />
        
    </bean>
        
    <bean id="dataSourceIndex" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            
    <!-- Connection Info -->
            
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            
    <property name="url" value="${jdbc.urlIndex}" />
            
    <property name="username" value="${jdbc.username}" />
            
    <property name="password" value="${jdbc.password}" />

            
    <!-- Connection Pooling Info -->
            
    <property name="initialSize" value="5" />
            
    <property name="maxActive" value="100" />
            
    <property name="maxIdle" value="30" />
            
    <property name="maxWait" value="1000" />
            
    <property name="poolPreparedStatements" value="true" />
            
    <property name="defaultAutoCommit" value="false" />
        
    </bean>

        
    <!-- 數據源配置,使用應用服務器的數據庫連接池 -->
        
    <!--<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/ExampleDB" />-->

        
    <!-- Hibernate配置 -->
        
    <bean id="sessionFactoryContent" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceContent" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>
        
    <bean id="sessionFactoryIndex" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceIndex" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>

        
    <!-- 事務管理器配置,單數據源事務 -->
        
    <bean id="transactionManagerContent" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            
    <property name="sessionFactory" ref="sessionFactoryContent" />
        
    </bean>
        
    <bean id="transactionManagerIndex" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            
    <property name="sessionFactory" ref="sessionFactoryIndex" />
        
    </bean>

        
    <!-- 事務管理器配置,多數據源JTA事務-->
        
    <!--
            <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager or
            WebLogicJtaTransactionManager" />
        
    -->

        
    <!-- 使用annotation定義事務 -->
        
    <tx:annotation-driven transaction-manager="transactionManagerContent" />
    </beans>

    這個時候運行還是會出錯,出錯的原因為org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'transactionManager' is defined,因為該出錯信息很短,我也難以找出究竟是哪個地方需要名為“transactionManager”的事務管理器 ,改個名字都不行,看來Spring的自動注入有時候也錯綜復雜害人不淺。不過,如果把上面的其中一個名字改成“transactionManger”,另外一個名字不改,運行是成功的,如下:
    <!-- 事務管理器配置,單數據源事務 -->
        
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            
    <property name="sessionFactory" ref="sessionFactoryContent" />
        
    </bean>
        
    <bean id="transactionManagerIndex" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            
    <property name="sessionFactory" ref="sessionFactoryIndex" />
        
    </bean>

    這個時候得出結論是,可以配置多個TransactionManager,但是必須有一個的名字是transactionManager。

    第四步、配置多個<tx:annotation-driven/>,如下:
        <!-- 使用annotation定義事務 -->
        
    <tx:annotation-driven transaction-manager="transactionManager" />
        
    <tx:annotation-driven transaction-manager="transactionManagerIndex" />

    運行測試,天啦,竟然成功了。和我之前預料的完全不一樣,居然在一個配置文件中配置多個<tx:annotation-driven/>一點問題都沒有。那么在使用@Transactional的地方,它真的能夠選擇正確的事務管理器嗎?我不得不寫更多的代碼來進行測試。那就針對索引數據庫中的表寫一個Entity,寫一個Dao測試一下吧。

    代碼如下:
    package cn.puretext.entity.web;

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Table;

    import org.hibernate.annotations.Cache;
    import org.hibernate.annotations.CacheConcurrencyStrategy;

    import cn.puretext.entity.IdEntity;

    @Entity
    // 表名與類名不相同時重新定義表名.
    @Table(name = "articles")
    // 默認的緩存策略.
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    public class ArticleIndex extends IdEntity {
        
    private String subject;
        
    private String webServer;

        
    public String getSubject() {
            
    return subject;
        }

        
    public void setSubject(String subject) {
            
    this.subject = subject;
        }
        @Column(name 
    = "webserver")
        
    public String getWebServer() {
            
    return webServer;
        }

        
    public void setWebServer(String webServer) {
            
    this.webServer = webServer;
        }
    }

    package cn.puretext.dao;

    import javax.annotation.Resource;

    import org.hibernate.SessionFactory;
    import org.springframework.stereotype.Repository;
    import org.springside.modules.orm.hibernate.HibernateDao;

    import cn.puretext.entity.web.ArticleIndex;

    @Repository
    public class ArticleIndexDao extends HibernateDao<ArticleIndex, Long> {
        @Override
        @Resource(name 
    = "sessionFactoryIndex")
        
    public void setSessionFactory(SessionFactory sessionFactory) {
            
    // TODO Auto-generated method stub
            super.setSessionFactory(sessionFactory);
        }

    }

     

    package cn.puretext.unit.service;

    import java.util.List;

    import javax.annotation.Resource;
    import javax.sql.DataSource;

    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.transaction.annotation.Transactional;
    import org.springside.modules.orm.Page;
    import org.springside.modules.test.junit4.SpringTxTestCase;

    import cn.puretext.dao.ArticleDao;
    import cn.puretext.dao.ArticleIndexDao;
    import cn.puretext.entity.web.Article;
    import cn.puretext.entity.web.ArticleIndex;
    import cn.puretext.service.ServiceException;

    public class DaoTest extends SpringTxTestCase {
        @Autowired
        
    private ArticleDao articleDao;
        @Autowired
        
    private ArticleIndexDao articleIndexDao;
        
        
    public void setArticleIndexDao(ArticleIndexDao articleIndexDao) {
            
    this.articleIndexDao = articleIndexDao;
        }

        
    public void setArticleDao(ArticleDao articleDao) {
            
    this.articleDao = articleDao;
        }

        @Override
        @Resource(name 
    = "dataSourceContent")
        
    public void setDataSource(DataSource dataSource) {
            
    // TODO Auto-generated method stub
            super.setDataSource(dataSource);
        }

        @Test
        @Transactional
        
    public void addArticle() {
            Article article 
    = new Article();
            article.setSubject(
    "article test");
            article.setContent(
    "article test");
            articleDao.save(article);
        }

        @Test
        @Transactional
        
    public void pageQuery() {
            Page
    <Article> page = new Page<Article>();
            page.setPageSize(
    10);
            page.setPageNo(
    2);
            page 
    = articleDao.getAll(page);
            List
    <Article> articles = page.getResult();
        }
        
        @Test
        @Transactional
        
    public void addIndex() {
            ArticleIndex articleIndex 
    = new ArticleIndex();
            articleIndex.setSubject(
    "test");
            articleIndex.setWebServer(
    "www001");
            articleIndexDao.save(articleIndex);
        }
        
        @Test
        @Transactional
        
    public void addArticleAndAddIndex() {
            addArticle();
            addIndex();
            
    throw new ServiceException("測試事務回滾");
        }
    }


    運行測試,結果還是成功的。到目前,發現在一個項目中使用多個TransactionManager可以正常運行,但是有兩個問題需要考慮:
    1、為什么必須得有一個TransactionManager名字為transactionManager?
    2、這兩個TransactionManager真的能正常工作嗎?
    3、OpenSessionInView的問題怎么解決?

    以上的三個問題在單元測試中是不能找出答案的,我只好再去寫Action層的代碼,期望能夠從中得到線索。經過一天艱苦的努力,終于真相大白:
    1、并不是必須有一個TransactionManager的名字為transactionMananger,這只是單元測試在搞鬼,在真實的Web環境中,無論兩個TransactionManager取什么名字都可以,運行不會報錯。所以這個答案很明確,是因為單元測試的基類需要一個名為transactionMananger的事務管理器。
    2、在單元測試中,只能測試Dao類和Entity類能否正常工作,但是由于單元測試結束后事務會自動回滾,不會把數據寫入到數據庫中,所以沒有辦法確定兩個TransactionManager能否正常工作。在真實的Web環境中,問題很快就浮出水面,只有一個數據庫中有數據,另外一個數據庫中沒有,經過調整<tx:annotation-driven/>的位置并對比分析,發現只有放在前面的TransactionMananger的事務能夠正常提交,放在后面的TransactionManager的事務不能提交,所以永遠只有一個數據庫里面有數據。
    3、如果早一點脫離單元測試而進入真實的Web環境,就會早一點發現OpenSessionInViewFilter的問題,因為只要配置多個SessionFactory,運行的時候OpenSessionInViewFilter就會報錯。為了解決這個問題,我只能去閱讀OpenSessionInViewFilter的源代碼,發現它在將Session綁定到線程的時候用的是Map,而且使用SessionFactory作為Map的key,這就說明在線程中綁定多個Session不會沖突,也進一步說明可以在web.xml中配置多個OpenSessionInViewFilter。而我也正是通過配置多個OpenSessionInViewFilter來解決問題的。我的web.xml文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation
    ="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

        
    <display-name>PureText</display-name>
        
    <!-- Spring ApplicationContext配置文件的路徑,可使用通配符,多個路徑用,號分隔
            此參數用于后面的Spring Context Loader 
    -->
        
    <context-param>
            
    <param-name>contextConfigLocation</param-name>
            
    <param-value>classpath*:/applicationContext*.xml</param-value>
        
    </context-param>

        
    <!-- Character Encoding filter -->
        
    <filter>
            
    <filter-name>encodingFilter</filter-name>
            
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            
    <init-param>
                
    <param-name>encoding</param-name>
                
    <param-value>UTF-8</param-value>
            
    </init-param>
            
    <init-param>
                
    <param-name>forceEncoding</param-name>
                
    <param-value>true</param-value>
            
    </init-param>
        
    </filter>

        
    <filter>
            
    <filter-name>hibernateOpenSessionInViewFilterContent</filter-name>
            
    <filter-class>org.springside.modules.orm.hibernate.OpenSessionInViewFilter</filter-class>
            
    <init-param>
                
    <param-name>excludeSuffixs</param-name>
                
    <param-value>js,css,jpg,gif</param-value>
            
    </init-param>
            
    <init-param>      
                   
    <param-name>sessionFactoryBeanName</param-name>
                
    <param-value>sessionFactoryContent</param-value>   
            
    </init-param>    
        
    </filter>
        
    <filter>
            
    <filter-name>hibernateOpenSessionInViewFilterIndex</filter-name>
            
    <filter-class>org.springside.modules.orm.hibernate.OpenSessionInViewFilter</filter-class>
            
    <init-param>
                
    <param-name>excludeSuffixs</param-name>
                
    <param-value>js,css,jpg,gif</param-value>
            
    </init-param>
            
    <init-param>      
                   
    <param-name>sessionFactoryBeanName</param-name>
                
    <param-value>sessionFactoryIndex</param-value>   
            
    </init-param>    
        
    </filter>
        
    <!-- SpringSecurity filter-->
        
    <filter>
            
    <filter-name>springSecurityFilterChain</filter-name>
            
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        
    </filter>

        
    <!-- Struts2 filter -->
        
    <filter>
            
    <filter-name>struts2Filter</filter-name>
            
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
        
    </filter>

        
    <filter-mapping>
            
    <filter-name>encodingFilter</filter-name>
            
    <url-pattern>/*</url-pattern>
        
    </filter-mapping>


        
    <filter-mapping>
            
    <filter-name>springSecurityFilterChain</filter-name>
            
    <url-pattern>/*</url-pattern>
        
    </filter-mapping>
        
    <filter-mapping>
            
    <filter-name>hibernateOpenSessionInViewFilterContent</filter-name>
            
    <url-pattern>/*</url-pattern>
        
    </filter-mapping>
        
    <filter-mapping>
            
    <filter-name>hibernateOpenSessionInViewFilterIndex</filter-name>
            
    <url-pattern>/*</url-pattern>
        
    </filter-mapping>
        
    <filter-mapping>
            
    <filter-name>struts2Filter</filter-name>
            
    <url-pattern>/*</url-pattern>
        
    </filter-mapping>

        
    <!--Spring的ApplicationContext 載入 -->
        
    <listener>
            
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        
    </listener>

        
    <!-- Spring 刷新Introspector防止內存泄露 -->
        
    <listener>
            
    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
        
    </listener>

        
    <!-- session超時定義,單位為分鐘 -->
        
    <session-config>
            
    <session-timeout>20</session-timeout>
        
    </session-config>

        
    <!-- 出錯頁面定義 -->
        
    <error-page>
            
    <exception-type>java.lang.Throwable</exception-type>
            
    <location>/common/500.jsp</location>
        
    </error-page>
        
    <error-page>
            
    <error-code>500</error-code>
            
    <location>/common/500.jsp</location>
        
    </error-page>
        
    <error-page>
            
    <error-code>404</error-code>
            
    <location>/common/404.jsp</location>
        
    </error-page>
        
    <error-page>
            
    <error-code>403</error-code>
            
    <location>/common/403.jsp</location>
        
    </error-page>
    </web-app>

    經過上面的分析,發現使用多個TransactionManager是不可行的(這個時候我在想,也許不使用Annotation就可以使用多個TransactionMananger吧,畢竟Spring的AOP應該是可以把不同的TransactionManager插入到不同的類和方法中,但是誰愿意走回頭路呢?畢竟都已經是@Transactional的年代了),雖然運行不會報錯,但是只有一個TransactionManager的事務能夠正常提交。所以測試進入下一步:

    第五步、使用JTATransactionManager

    簡單地修改配置文件,使用JTATransactionManager做為事務管理器,配置文件我就不列出來了,運行,結果抱錯,錯誤信息如下:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainProxy': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainList': Cannot create inner bean '(inner bean)' of type [org.springframework.security.config.OrderedFilterBeanDefinitionDecorator$OrderedFilterDecorator] while setting bean property 'filters' with key [10]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)': Cannot resolve reference to bean 'filterSecurityInterceptor' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'filterSecurityInterceptor' defined in file [D:\Temp\1-PureText\WEB-INF\classes\applicationContext-security.xml]: Cannot resolve reference to bean 'databaseDefinitionSource' while setting bean property 'objectDefinitionSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'databaseDefinitionSource': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.interceptor.TransactionInterceptor#0': Cannot resolve reference to bean 'transactionManager' while setting bean property 'transactionManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in file [D:\Temp\1-PureText\WEB-INF\classes\applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: No JTA UserTransaction available - specify either 'userTransaction' or 'userTransactionName' or 'transactionManager' or 'transactionManagerName'

    通過分析,發現其中最關鍵的一句是No JTA UserTransaction available,看來,我們只能進入到第六步,使用GlassFish了。

    第六步、將項目部署到GlassFish中

    將項目簡單地部署到GlassFish中之后,項目可以成功運行,沒有報錯,說明JTA UserTransaction問題解決了,但是檢查數據庫卻發現依然沒有數據,看來JTATransactionManager不僅要和應用服務器配合使用,還要和JNDI數據源一起使用。將數據源的配置修改為JNDI后,問題解決。下面是我的配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jee
    ="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context
    ="http://www.springframework.org/schema/context"
        xsi:schemaLocation
    ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
        default-lazy-init
    ="true">

        
    <description>Spring公共配置文件 </description>

        
    <!-- 定義受環境影響易變的變量 -->
        
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
            
    <property name="ignoreResourceNotFound" value="true" />
            
    <property name="locations">
                
    <list>
                    
    <!-- 標準配置 -->
                    
    <value>classpath*:/application.properties</value>
                    
    <!-- 本地開發環境配置 -->
                    
    <value>classpath*:/application.local.properties</value>
                    
    <!-- 服務器生產環境配置 -->
                    
    <!-- <value>file:/var/myapp/application.server.properties</value> -->
                
    </list>
            
    </property>
        
    </bean>

        
    <!-- 使用annotation 自動注冊bean,并保證@Required,@Autowired的屬性被注入 -->
        
    <context:component-scan base-package="cn.puretext" />

        
    <!-- 數據源配置,使用應用服務器的數據庫連接池 -->
        
    <jee:jndi-lookup id="dataSourceContent" jndi-name="jdbc/dataSourceContent" />
        
    <jee:jndi-lookup id="dataSourceIndex" jndi-name="jdbc/dataSourceIndex" />

        
    <!-- Hibernate配置 -->
        
    <bean id="sessionFactoryContent" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceContent" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>
        
    <bean id="sessionFactoryIndex" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            
    <property name="dataSource" ref="dataSourceIndex" />
            
    <property name="namingStrategy">
                
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
            
    </property>
            
    <property name="hibernateProperties">
                
    <props>
                    
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                    
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                    
    </prop>
                    
    <prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
                
    </props>
            
    </property>
            
    <property name="packagesToScan" value="cn.puretext.entity.*" />
        
    </bean>

        
    <!-- 事務管理器配置,單數據源事務 -->
        
    <!--
        <bean id="transactionManagerContent" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactoryContent" />
        </bean>
        <bean id="transactionManagerIndex" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactoryIndex" />
        </bean>
        
    -->
        
        
    <!-- 事務管理器配置,多數據源JTA事務-->
        
        
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
        

        
    <!-- 使用annotation定義事務 -->
        
    <tx:annotation-driven transaction-manager="transactionManager" />
        
    </beans>

    最后,我得出的結論是:要想使用多個數據庫,就必須使用JTATransactionMananger,必須使用GlassFish等應用服務器而不是Tomcat,必須使用JNDI來管理dataSource。

    如果一定要使用Tomcat呢?

    這確實是一個難題,但是并不代表著沒有解決辦法。經過廣泛的Google一番之后,終于發現了一個好東東,那就是JOTM,它的全稱就是Java Open Transaction Mananger,它的作用就是可以單獨提供JTA事務管理的功能,不需要應用服務器。JOTM的使用方法有兩種,一種就是把它配置到項目中,和Spring結合起來使用,另外一種就是把它配置到Tomcat中,這時,Tomcat搖身一變就成了和GlassFish一樣的能夠提供JTA功能的服務器了。

    JOTM的官方網站為http://jotm.ow2.org,這是它的新網站,舊網站為http://jotm.objectweb.org

    我選擇了把JOTM 2.0.11整合到Tomcat中的方法進行了測試,結果發現還是不能夠正常運行,我使用的是JOTM2.0.11,Tomcat 6.0.20,JKD 6 Update10。看來還得繼續折騰下去了。

    另外一個開源的JTA事務管理器是Atomikos,它供了事務管理和連接池,不需要應用服務器支持,其官方網站為http://www.atomikos.com/。有興趣的朋友可以試試。


    評論

    # re: SpringSide 3 中的多數據源配置  回復  更多評論   

    2009-07-13 22:03 by 虎嘯龍吟
    能搞定?還沒看明白

    # re: SpringSide 3 中的多數據源配置  回復  更多評論   

    2009-07-14 14:47 by lsqlister
    寫的很好。
    問題1:spring中默認不是要配一個transactionManager嗎?

    # re: SpringSide 3 中的多數據源配置的問題  回復  更多評論   

    2009-07-24 22:40 by 虎嘯龍吟
    到底tomcat能不能支持多數據源實現啊?

    # re: SpringSide 3 中的多數據源配置的問題  回復  更多評論   

    2009-07-25 12:56 by 海邊沫沫
    單獨的Tomcat不行

    加上Atomikos就可以。

    # re: SpringSide 3 中的多數據源配置的問題  回復  更多評論   

    2009-07-26 10:32 by 虎嘯龍吟
    明白了

    # re: SpringSide 3 中的多數據源配置的問題  回復  更多評論   

    2009-12-14 13:16 by 夫子一品
    樓主專研的精神不錯,值得學習!

    # re: SpringSide 3 中的多數據源配置的問題[未登錄]  回復  更多評論   

    2009-12-22 17:52 by Harry
    樓主太好了,給我了很多思路

    # re: SpringSide 3 中的多數據源配置的問題[未登錄]  回復  更多評論   

    2010-08-03 13:09 by Aaronlong31
    我之前使用iBATIS+spring+Atomikos成功過。

    # re: SpringSide 3 中的多數據源配置的問題  回復  更多評論   

    2011-05-17 15:52 by 省時
    tomcat下 Error creating bean with name '_filterChainProxy': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainList': Cannot create inner bean '(inner bean)' of type 這個問題解決沒?

    # re: SpringSide 3 中的多數據源配置的問題[未登錄]  回復  更多評論   

    2011-12-09 17:22 by hot
    但是好像spring3.0(具體版本沒弄清)之前的Transactional標注不支持區分使用哪個事務管理器。3.0之后的版本Transactional增加了個string類型的value屬性來特殊指定加以區分。

    # re: SpringSide 3 中的多數據源配置的問題[未登錄]  回復  更多評論   

    2011-12-09 17:24 by hot
    如果調換兩個事務管理器在容器中的定義順序,如
    <tx:annotation-driven transaction-manager="transactionManagerY" />
    <tx:annotation-driven transaction-manager="transactionManagerX" />
    得到的結果
    methodX()事務生效測試結果 @Transactional
    ("transactionManagerX")
    @Transactional
    ("transactionManagerY")
    @Transactional
    ("transactionManagerZ")
    transactionManagerZ為未定義過的 @Transactional
    TestEntityServiceImpl(實際使用datasourceX) Y N N N
    AnotherTestEntityServiceImpl (實際使用datasourceY) N Y Y Y


    分析結果(其實源碼就可以反應出):容器指定一個默認的事務管理器
    1.當在@Transactional("xxx")中正確指定了需要使用的事務管理器時,事務控制正常。
    2.如果@Transactional指定了未定義過的事務管理器,spring以缺省默認的事務管理器來處理。(如果程序正好使用的是缺省事務管理器同一個數據源,事務控制將生效)。
    3.如果@Transactional不指定事務管理器,使用缺省。
    4.如果@Transactional指定了不匹配的事務管理器(實際用到的數據源和指定的事務管理器控制的數據源不一致),事務控制將失效.
    主站蜘蛛池模板: 亚洲色婷婷六月亚洲婷婷6月| 24小时在线免费视频| 九九综合VA免费看| 国产精品亚洲片在线花蝴蝶| 亚洲人成色在线观看| 亚洲国产欧洲综合997久久| 亚洲AV成人无码网站| 美女视频黄频a免费大全视频| 精品亚洲国产成人av| 美女被吸屁股免费网站| 免费无码午夜福利片69| 4hu四虎免费影院www| 免费久久人人爽人人爽av | 成人精品综合免费视频| 国产高潮流白浆喷水免费A片 | 在线免费视频你懂的| 免费污视频在线观看| 四虎免费影院ww4164h| 国拍在线精品视频免费观看 | 免费精品无码AV片在线观看| 3d动漫精品啪啪一区二区免费| 无码少妇一区二区浪潮免费| 日韩激情淫片免费看| 国产成人高清亚洲| 亚洲AV日韩精品久久久久| 亚洲福利视频网址| 欧美日韩亚洲精品| 99精品视频在线观看免费| 在线成人爽a毛片免费软件| 成人无码区免费视频观看| 免费乱理伦在线播放| 亚洲精品自在在线观看| 亚洲激情黄色小说| 国产精品久久久久久亚洲影视 | 国产成人精品日本亚洲18图| 国产成人亚洲午夜电影| 免费91麻豆精品国产自产在线观看 | 亚洲国产综合在线| 国产成人人综合亚洲欧美丁香花| fc2免费人成为视频| 67194国产精品免费观看|