近期由于业务需求需在在项目中配置多一个db链接,项目的的架构是ssh,需要在spring中配置多数据源,废话不多说。
先看配置文件spring配置文件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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://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.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- 定义父数据源指定公共的属性 -->
<bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="maxActive" value="25000"></property>
<property name="maxIdle" value="30"></property>
<property name="maxWait" value="300"></property>
</bean>
<!-- 定义数据源1指定私有的属性 -->
<bean id="adminDataSource" parent="parentDataSource">
<property name="url"
value="jdbc:mysql://192.168.1.8:3306/hyi?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&noAccessToProcedureBodies=true&useCompression=true">
</property>
<property name="username" value="root"></property>
<property name="password" value="henyep"></property>
</bean>
<!-- 定义数据源2指定私有的属性 -->
<bean id="userDataSource" parent="parentDataSource">
<property name="url"
value="jdbc:mysql://192.168.1.210:3306/INFORMATION_RELEASE?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&noAccessToProcedureBodies=true&useCompression=true">
</property>
<property name="username" value="development"></property>
<property name="password" value="2011dev"></property>
</bean>
<!-- 动态数据源 -->
<bean id="dataSource" class="com.infoCollect.common.DynamicDataSource"> //注意这个类是我们自定义的重写了spring封装类的一个方法,具体的看后续类介绍
<property name="defaultTargetDataSource" ref="adminDataSource"/> <!-- 默认使用admin的数据源 -->
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="user" value-ref="userDataSource"></entry> <!-- 加载user的数据源,可以添加更多 -->
</map>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<!-- 给hibernate配置属性 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.jdbc.fetch_size">25</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
</props>
</property>
<!-- 映射文件 下面两个文件分别是两个db中的表 -->
<property name="mappingResources">
<list>
<value>com/infoCollect/hbm/country.hbm.xml</value>
<value>com/infoCollect/hbm/TbUserManager.hbm.xml</value>
</list>
</property>
</bean>
<!-- 配置HIBERNATE基本的dao操作方法 -->
<bean id="ShareDao" class="com.infoCollect.common.ShareDao" abstract="true">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<bean id="sdaobean" class="com.infoCollect.common.ShareDao">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- 使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入 -->
<!-- 引入对应的关联注入文件 -->
<import resource="/com/infoCollect/springXml/applicationContext-test.xml"/>
<!-- 事物管理 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 给事物添加的属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- methods starting with 'save', 'update' or 'remove' use the default transaction settings -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by specified interface -->
<aop:config>
<aop:pointcut id="daoOperation"
expression="execution(* com.infoCollect.common.ShareDao.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation"/>
</aop:config>
</beans>
接下来创建一个数据源切换类,获得和设置上下文,主要负责设置上下文环境和获得上下文环境。
package com.infoCollect.common;
/***
* 项目名称:hym2.0
* <p>
* 功能模块名称:
* <p>
* 文件名称为:ConfigSource.java
* <p>
* 文件功能简述:获得和设置上下文,主要负责设置上下文环境和获得上下文环境
* <p>
* 开发者:——张建明
* <p>
* 联系QQ:78098702
* <p>
* 联系电话:13418583587
* <p>
* 电子邮件:shenhua_66666@126.com
* <p>
* 备注:
* @version v1.0
* @copyright henyep
*/
public class ConfigSource {
public static final ThreadLocal contextHolder = new ThreadLocal();
/**
*
* 功能描述:设置数据源
* @param datasource
*/
public static void useDataSource(String datasource){
contextHolder.set(datasource);
}
/**
*
* 功能描述:获取数据源
* @return
*/
public static String getDataSourceType(){
return (String)contextHolder.get();
}
/**
*
* 功能描述:移除数据源
*/
public static void clearDataSource(){
contextHolder.remove();
}
}
继续创建一个类DataSourceMap主要为为了配置数据源常量
package com.infoCollect.common;
public class DataSourceMap {
public static String datasource1="admin"; //这里的值 要跟 配置文件中的key value-ref对应起来 需谨慎
public static String datasource2="user";
}
然后就是核心的原理了,了解过spring获取sessionfactroy机制的都清楚 spring能维护数据源和db事物是因为spring封装了AbstractRoutingDataSource这个类。spring功过这个类来获取具体该使用哪个数据源,所以我们要重写AbstractRoutingDataSource中的一个方法determineCurrentLookupKey() ,看下面的类
package com.infoCollect.common;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 这个类实现了determineCurrentLookupKey方法,该方法返回一个Object,
* 一般是返回字符串,也可以是枚举类型。该方法中直接使用了ConfigSource.getCustomerType()方法获得上下文环境并直接返回。
*
*/
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return ConfigSource.getDataSourceType();
}
}
做到这里多数据源的配置已经完成,这里我只配置了两个数据源 分别连接了2个mysql,具体怎么使用呢,下面我觉个实例来测试一下是否能按切换数据源来分别的操作两个db。其实最关键的是dao类。action service 以及层层注入跟ssh耦合方式是一样的。
下面给出两个dao,分别是操作两个db中的表
第一个 查询db1中Hyicountry表
package com.infoCollect.dao;
import java.util.ArrayList;
import java.util.List;
import com.infoCollect.bean.Hyicountry;//实体bean
import com.infoCollect.common.ConfigSource;//数据源配置类 上面提到过
import com.infoCollect.common.DataSourceMap;//数据源常量类
import com.infoCollect.common.ShareDao;//底层的dao用来 crud
public class TestCountryDao extends ShareDao<Hyicountry, Integer> implements InterTestCountryDao{
public List<Hyicountry> getList(){
ConfigSource.useDataSource(DataSourceMap.datasource1); //这句最主要,告诉spring来使用第一个数据源
List<Hyicountry> list = new ArrayList<Hyicountry>();
String hql ="from Hyicountry";
list = super.find(hql);
return list;
}
}
第二个dao 查询db2中TbUserManager表
package com.infoCollect.dao;
import java.util.ArrayList;
import java.util.List;
import com.infoCollect.bean.TbUserManager;
import com.infoCollect.common.ConfigSource;
import com.infoCollect.common.DataSourceMap;
import com.infoCollect.common.ShareDao;
public class UserManagerDao extends ShareDao<TbUserManager, Integer> implements InterUserManagerDao{
public List<TbUserManager> getList(){
ConfigSource.useDataSource(DataSourceMap.datasource2);
List<TbUserManager> list = new ArrayList<TbUserManager>();
String hql ="from TbUserManager";
list = super.find(hql);
return list;
}
}
再贴出action吧
public class TestAction extends ActionSupport{
private InterTestCountryDao zyDao;
private InterUserManagerDao zyUserDao;
//从db1中取数据
public String testhyi(){
List<Hyicountry> list = zyDao.getList();
System.out.println("---"+list.size());
return SUCCESS;
}
//从db2中取数据
public String testNews(){
List<TbUserManager> list = zyUserDao.getList();
System.out.println("--+++-"+list.size());
return SUCCESS;
}
请求两次 结果如下
--+++-10
---237
到这里基本可用,至于性能,线程安全,事务处理是否完美 以及其他可优化的余地 仍需要研究,明天研究后再与同仁分享。