Spring多数据源管理

环境

1. Tomcat 6.0.28

2. Spring 2.5, SpringSide 2.5

3. Hibernate

业务需求

1. Spring 管理多个数据源,针对通应用有效促进数据库分库,分用户

2. 分布式事务控制

3. 通过JNDI来获取数据源,程序员无需关系数据库相关配置

4. 抽取低层框架,用户权限集中管理

步骤

Tomcat配置

1. Tomcat有两种配置数据源的方式,

 第一种:全局数据库连接池,

 1. CATALINA_HOME/conf/context.xml, 这种称为全局资源,可以配置多个,推荐使用。

<Resource name="jdbc/ghlf" 
 auth="Container"
 type="javax.sql.XADataSource"
 factory="org.objectweb.jotm.datasource.DataSourceFactory"
 password="admin"
 username="root"
 driverClassName="com.mysql.jdbc.Driver"
 url="jdbc:mysql://127.0.0.1:3306/ghlf"
 maxActive="100"
 maxIdle="30"   
 maxWait="5000"
 />   2. CATALINA_HOME/conf/server.xml, 这种可以看成是通过管理界面来配置的数据库连接池。
 <Resource name="jdbc/mydb" type="javax.sql.DataSource"
  username="bmgis" password="bmgis"
  driverClassName="oracle.jdbc.driver.OracleDriver"
  url="jdbc:oracle:thin:@localhost:1521:mydb"
  maxIdle="2" maxWait="5000" maxActive="4"/>


 第二种:局部数据库连接池,CATALINA_HOME/webapps/META-INF/context.xml,


应用数据源引用

   

<resource-ref>  
       <res-ref-name>jdbc/framework</res-ref-name>  
       <res-type>javax.sql.DataSource</res-type>  
       <res-auth>Container</res-auth>  
   </resource-ref> 
      
   <resource-ref>  
       <res-ref-name>jdbc/ghlf</res-ref-name>  
       <res-type>javax.sql.DataSource</res-type>  
       <res-auth>Container</res-auth>  
   </resource-ref>


JNDI引用

在spring application.xml中

<bean id="dataSource_framework" class="org.springframework.jndi.JndiObjectFactoryBean" > 
      <property name="jndiName" value="java:comp/env/jdbc/framework"></property>  
  </bean>
  
     
  <bean id="dataSource_ghlf" class="org.springframework.jndi.JndiObjectFactoryBean" > 
      <property name="jndiName" value="java:comp/env/jdbc/ghlf"></property>  
  </bean>


配置sessionfactory

在应用中用到hibernate这个ORM框架,所以必须要将spring与hibernate集成

<bean id="sessionFactory_framework"
   class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
   <property name="dataSource" ref="dataSource_framework" />
   <property name="namingStrategy">
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
   </property>
   <property name="hibernateProperties">
    <props>
     <prop key="hibernate.dialect">
      ${hibernate.dialect}
     </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.transfar._group.framework.entities,cn.transfar._group.framework.entities.*" />
  </bean>    <bean id="sessionFactory_ghlf"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
   <property name="dataSource" ref="dataSource_ghlf" />
   <property name="namingStrategy">
    <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
   </property>
   <property name="hibernateProperties">
    <props>
     <prop key="hibernate.dialect">
      ${hibernate.dialect}
     </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.transfar._group._appname.entities,cn.transfar._group._appname.entities.*" />
  </bean>


事务管理

由于应用中用到的是annotation,事务控制全部在业务层去显示申明,并且在多数据源的环境中,jdbc本地事务是没有办法控制的,必须引用JTA全局事务,这样才能保证多个数据源的数据一致性。由此带来了新的问题,tomcat6是不支持JTA,不比jboss,和websphere等容器,所以必须借助额外的插件或者框架,这里以Jotm (java open transaction manager)为例。

<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
  
     <!--  transaction manager definition-->
  <bean id="transactionManager"  class="org.springframework.transaction.jta.JtaTransactionManager">
       <property name="userTransaction" ref="jotm" />
  </bean> 
     
     <!-- auto manage the transaction -->
  <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

OpensessionInView

必须为多个数据源开启OpenSessionInView模式,这样在lazycelue环境中合理的管理会话,当然这种模式提供了多个配置支持

<filter>
   <filter-name>hibernateOpenSessionInViewFilterFramework</filter-name>
   <filter-class>org.springframework.orm.hibernate3.support.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>sessionFactory_framework</param-value>          </init-param>   
  </filter>
  
  
  <filter>
   <filter-name>hibernateOpenSessionInViewFilterGHLF</filter-name>
   <filter-class>org.springframework.orm.hibernate3.support.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>sessionFactory_ghlf</param-value>          </init-param>   
  </filter>

DAO实现指定数据源
由于springside集成了各种数据库操作方式,使用很方便,这里以他为例,
@Repository
 public class FriendDAO extends HibernateDao<Friend, String>{ 
  @Override
  @javax.annotation.Resource(name="sessionFactory_ghlf")
  public void setSessionFactory(SessionFactory sessionFactory) {
   super.setSessionFactory(sessionFactory);
  }


}


分析

经过上面的配置基本可以运行,但是经过测试发现还有如下问题待解决:

1. JTA事务在异常的时候未能回滚

2. lazy加载存在问题

解决方案

1. 经过问题的错误及排查,可以肯定的是在数据元的配置上面,可能需要我们关注这两者的差别DataSource 和XADataSource, 前者在JTA环境下确实没有办法管理JTA事务,后者在JTA上面才支持事务,由此我们必须要启用XADataSource。

<Resource name="jdbc/ghlf" 
 auth="Container"
 type="javax.sql.XADataSource"
 factory="org.objectweb.jotm.datasource.DataSourceFactory"
 password="admin"
 username="root"
 driverClassName="com.mysql.jdbc.Driver"
 url="jdbc:mysql://127.0.0.1:3306/ghlf"
 maxActive="100"
 maxIdle="30"   
 maxWait="5000"
 />


2. lazy加载问题出现主要是因为我们使用了spring-security来做权限管理,由此带来配置问题规范问题,我们前端主要用到的框架如下: structs,spring-security ,openSessionInView,siteMap,因此这几个的初始配置顺序是要考虑的。

<filter-mapping>
   <filter-name>encodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter-mapping>
   <filter-name>hibernateOpenSessionInViewFilterFramework</filter-name>
   <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter-mapping>
   <filter-name>hibernateOpenSessionInViewFilterGHLF</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>sitemesh</filter-name>
         <url-pattern>*.action</url-pattern>
         <url-pattern>*.jsp</url-pattern>
     </filter-mapping>
     
  
  <filter-mapping>
   <filter-name>struts2Filter</filter-name>
   <url-pattern>*.action</url-pattern>
   <dispatcher>REQUEST</dispatcher>
   <dispatcher>FORWARD</dispatcher>
  </filter-mapping>