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>