1.Spring 配置文件datasource
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource" destroy-method="close">
<!-- 指定连接数据库的URL-->
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/ConfigurationRepository?&autoReconnect=true&characterEncoding=utf8" />
<!-- 指定连接数据库的驱动-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定连接数据库的用户名-->
<property name="user" value="root"/>
<!-- 指定连接数据库的密码-->
<property name="password" value="root"/>
<!-- 指定连接池中保留的最大连接数. Default:15-->
<property name="maxPoolSize" value="20"/>
<!-- 指定连接池中保留的最小连接数-->
<property name="minPoolSize" value="3"/>
<!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->
<property name="autoCommitOnClose" value="false" />
<!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->
<property name="checkoutTimeout" value="3000"/>
<!-- 指定连接池的初始化连接数 取值应在minPoolSize 与 maxPoolSize 之间.Default:3-->
<property name="initialPoolSize" value="3"/>
<!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。 Default:0-->
<property name="maxIdleTime" value="60"/>
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数. Default:3-->
<property name="acquireIncrement" value="2"/>
<!-- JDBC的标准,用以控制数据源内加载的PreparedStatements数量。
但由于预缓存的statements属于单个connection而不是整个连接池所以设置这个参数需要考虑到多方面的因数.如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0-->
<property name="maxStatements" value="100"/>
<!-- 每60秒检查所有连接池中的空闲连接.Default:0 -->
<property name="idleConnectionTestPeriod" value="1"/>
</bean>
<!-- 动态数据源配置 -->
<bean id="dynamicDataSource" class="org.mybatis.Dynamic.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 动态初始化map -->
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource" />
</bean>
<bean id="applicationEventListener" class="org.mybatis.Dynamic.DynamicCreateDataSourceBean">
<property name="dynamicDataSource" ref="dynamicDataSource"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<span style="white-space:pre"> </span><!--动态数据源配置-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dynamicDataSource" ></property>
</bean>
<!-- 事务管理器配置, 使用jdbc事务 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="query*" read-only="true" />
<tx:method name="is*" read-only="true" />
<tx:method name="do*" propagation="REQUIRES_NEW"
rollback-for="java.lang.Exception" />
<tx:method name="save*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<!-- 事务拦截的类及方法 -->
<aop:config proxy-target-class="true">
<!-- 其中第一个*代表返回值,第二*代表service下子包,第三个*代表方法名,“(..)”代表方法参数。 -->
<aop:advisor pointcut="execution(* xt.city.edi.*.*(..))"
advice-ref="txAdvice" />
</aop:config>
【动态设置数据源的上下文类】
package org.mybatis.Dynamic;
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setDbType(String dbType) {
contextHolder.set(dbType);
}
public static String getDbType() {
return ((String) contextHolder.get());
}
public static void clearDbType() {
contextHolder.remove();
}
}
【动态创建数据源类】
package org.mybatis.Dynamic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 实现一个实现ApplicationContextAware和ApplicationListener接口的类DynamicDataSourceC3p0,
* 实现ApplicationContextAware是为了得到ApplicationContext,
* 实现了ApplicationListener是为了配置spring的加载事件。
*
*/
public class DynamicCreateDataSourceBean implements ApplicationContextAware,ApplicationListener{
private ConfigurableApplicationContext app;
private DynamicDataSource dynamicDataSource;
public DynamicDataSource getDynamicDataSource() {
return dynamicDataSource;
}
public void setDynamicDataSource(DynamicDataSource dynamicDataSource) {
this.dynamicDataSource = dynamicDataSource;
}
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setApplicationContext(ApplicationContext app) throws BeansException {
this.app = (ConfigurableApplicationContext)app;
}
public void onApplicationEvent(ApplicationEvent event) {
// 如果是容器刷新事件OR Start Event
if (event instanceof ContextRefreshedEvent) {
try {
regDynamicBean();
} catch (IOException e) {
e.printStackTrace();
}
//System.out.println(event.getClass().getSimpleName()+" 事件已发生!");
}
}
private void regDynamicBean() throws IOException {
// 解析属性文件,得到数据源Map
Map<String, DataSourceInfo> mapCustom = parsePropertiesFile();
addSourceBeanToApp2(mapCustom);
// 把数据源bean注册到容器中
/*if(getDataSource() instanceof ComboPooledDataSource){
addSourceBeanToApp2(mapCustom);
}else{*/
// addSourceBeanToApp(mapCustom);
/*}*/
}
/**
* 功能说明:根据DataSource创建bean并注册到容器中
* @param acf
* @param mapCustom
*/
private void addSourceBeanToApp2(Map<String, DataSourceInfo> mapCustom) {
DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app.getAutowireCapableBeanFactory();
BeanDefinition beanDefinition;
Iterator<String> iter = mapCustom.keySet().iterator();
Map<Object,Object> targetDataSources = new LinkedHashMap<Object, Object>();
while(iter.hasNext()){
String beanKey = iter.next();
// 得到Bean定义,并添加到容器中
beanDefinition = new ChildBeanDefinition("dataSource");
// 注意:必须先注册到容器中,再得到Bean进行修改,否则数据源属性不能有效修改
acf.registerBeanDefinition(beanKey, beanDefinition);
// 再得到数据源Bean定义,并修改连接相关的属性
ComboPooledDataSource cpds = (ComboPooledDataSource)app.getBean(beanKey);;
cpds.setJdbcUrl(mapCustom.get(beanKey).connUrl);
cpds.setUser(mapCustom.get(beanKey).userName);
cpds.setPassword(mapCustom.get(beanKey).password);
targetDataSources.put(beanKey, app.getBean(beanKey));
}
//将创建的map对象set到 targetDataSources;
dynamicDataSource.setTargetDataSources(targetDataSources);
//必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效
dynamicDataSource.afterPropertiesSet();
}
/**
* 功能说明:根据DataSource创建bean并注册到容器中
* @param mapCustom
*/
private void addSourceBeanToApp(Map<String, DataSourceInfo> mapCustom) {
DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app
.getAutowireCapableBeanFactory();
String DATASOURCE_BEAN_CLASS = "org.apache.commons.dbcp.BasicDataSource";
String DATASOURCE_BEAN_CLASS2 = "com.mchange.v2.c3p0.ComboPooledDataSource";
BeanDefinitionBuilder bdb;
Iterator<String> iter = mapCustom.keySet().iterator();
Map<Object,Object> targetDataSources = new LinkedHashMap<Object, Object>();
//ChildBeanDefinition baseBeanComm = new ChildBeanDefinition("dataSource");
// 将默认数据源放入 targetDataSources map中
//targetDataSources.put("dataSource", app.getBean("dataSource"));
// 根据数据源得到数据,动态创建数据源bean 并将bean注册到applicationContext中去
while (iter.hasNext()) {
// bean ID
String beanKey = iter.next();
// 创建bean
bdb = BeanDefinitionBuilder.childBeanDefinition("dataSource");
bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS2);
bdb.getBeanDefinition().setAttribute("id", beanKey);
//bdb.addPropertyValue("driverClass", "com.mysql.jdbc.Driver");
bdb.addPropertyValue("jdbcUrl", mapCustom.get(beanKey).connUrl);
bdb.addPropertyValue("user", mapCustom.get(beanKey).userName);
bdb.addPropertyValue("password", mapCustom.get(beanKey).password);
// 注册bean
acf.registerBeanDefinition(beanKey, bdb.getBeanDefinition());
// 放入map中,注意一定是刚才创建bean对象
targetDataSources.put(beanKey, app.getBean(beanKey));
}
// 将创建的map对象set到 targetDataSources;
dynamicDataSource.setTargetDataSources(targetDataSources);
// 必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效
dynamicDataSource.afterPropertiesSet();
}
/**
* 功能说明:GET city.properties
*
* @return
* @throws IOException
*/
private Map<String, DataSourceInfo> parsePropertiesFile()
throws IOException {
InputStream ips2 = this.getClass().getClassLoader() .getResourceAsStream("city.properties");
InputStreamReader isr=new InputStreamReader(ips2, "UTF-8");
BufferedReader br = new BufferedReader(isr);
Map<String, DataSourceInfo> mds = new HashMap<String, DataSourceInfo>();
String line = "";
while(( line = br.readLine())!=null){
String[] keyVal = line.split(".url=");
DataSourceInfo dsi = new DataSourceInfo();
String[] connStr = StringUtils.split(keyVal[1],"|");
System.out.println(connStr[0]);
System.out.println(connStr[1]);
System.out.println(connStr[2]);
dsi.connUrl = connStr[0];
dsi.userName = connStr[1].split("=")[1];
dsi.password = connStr[2].split("=")[1];
mds.put(keyVal[0], dsi);
}
br.close();
isr.close();
ips2.close();
return mds;
}
// 自定义数据结构
private class DataSourceInfo{
public String connUrl;
public String userName;
public String password;
public String toString() {
return "(url:" + connUrl + ", username:" + userName + ", password:"
+ password + ")";
}
}
}
Spring 中自动调用的类
package org.mybatis.Dynamic;
import java.util.logging.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
static Logger log = Logger.getLogger("DynamicDataSource");
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDbType();
}
}
调用逻辑请看[AbstractRoutingDataSource.java]
使用方法: DataSourceContextHolder.setDbType("datasourceName");"datasourceName"为动态创建的数据源bean名称