应用场景:查询业务为主,多个数据源,根据不同的请求,切换到不同的数据源;
1:创建DynamicDataSource
扩展一个Spring提供的AbstractRoutingDataSource,Override 其中的 determineCurrentLookupKey方法实现数据源的路由
import java.sql.SQLException; import javax.sql.DataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.util.Assert; /** * Dynamic DataSource * */ public class DynamicDataSource extends AbstractRoutingDataSource { //@Override protected Object determineCurrentLookupKey() { return CustomerContextHolder.getCustomerType(); } }
2:创建CustomerContextHolder
CustomerContextHolder这是开发人员自己实现的一个封装了ThreadLocal类型的ContextHolder。
public class CustomerContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setCustomerType(String customerType) { contextHolder.set(customerType); } public static String getCustomerType() { return contextHolder.get(); } public static void clearCustomerType() { contextHolder.remove(); } }
3:bean.xml
xml 中有一个默认的数据源,多数据库的信息在默认数据源表中存放着;
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" > <property name="driverClassName"> <value>org.gjt.mm.mysql.Driver</value> </property> <property name="url"> <value>jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>root</value> </property> <property name="timeBetweenEvictionRunsMillis"> <value>3600000</value> </property> <property name="minEvictableIdleTimeMillis"> <value>3600000</value> </property> </bean> <!-- S --> <bean id="dynamicDataSource" class="common.DynamicDataSource" > <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- 动态初始化map--> </map> </property> <property name="defaultTargetDataSource" ref="dataSource" > </property> </bean> <!-- E --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" > <property name="dataSource" ref="dynamicDataSource" ></property> </bean>
启动时,需要用到默认数据源资源,从其表中得到数据,从而创建新的数据源bean
<bean id="applicationEventListener" class="common.DynamicCreateBean"> <property name="dynamicDataSource" ref="dynamicDataSource" ></property> <property name="jdbcTemplate" ref="jdbcTemplate" ></property> </bean >
4:DynamicCreateDataSourceBean
动态创建datasourcebean
import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.aop.target.HotSwappableTargetSource; 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; /** * 实现一个实现ApplicationContextAware和ApplicationListener接口的类DynamicDataSourceC3p0, * 实现ApplicationContextAware是为了得到ApplicationContext, * 实现了ApplicationListener是为了配置spring的加载事件。 * */ public class DynamicCreateDataSourceBean implements ApplicationContextAware,ApplicationListener{ private ConfigurableApplicationContext app; private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } private DynamicDataSource dynamicDataSource; public void setDynamicDataSource(DynamicDataSource dynamicDataSource) { this.dynamicDataSource = dynamicDataSource; } 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(); // 把数据源bean注册到容器中 addSourceBeanToApp(mapCustom); } /** * 功能说明:根据DataSource创建bean并注册到容器中 * * @param acf * @param mapCustom */ private void addSourceBeanToApp(Map<String, DataSourceInfo> mapCustom) { DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app .getAutowireCapableBeanFactory(); String DATASOURCE_BEAN_CLASS = "org.apache.commons.dbcp.BasicDataSource"; BeanDefinitionBuilder bdb; Iterator<String> iter = mapCustom.keySet().iterator(); Map<String,Object> targetDataSources = new LinkedHashMap<String, Object>(); baseBeanComm = new ChildBeanDefinition("dataSource"); // 将默认数据源放入 targetDataSources map中 targetDataSources.put("dataSourceA", app.getBean("dataSource")); // 根据数据源得到数据,动态创建数据源bean 并将bean注册到applicationContext中去 while (iter.hasNext()) { // bean ID String beanKey = iter.next(); // 创建bean bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS); bdb.getBeanDefinition().setAttribute("id", beanKey); bdb.addPropertyValue("driverClassName", "org.gjt.mm.mysql.Driver"); bdb.addPropertyValue("url", mapCustom.get(beanKey).connUrl); bdb.addPropertyValue("username", mapCustom.get(beanKey).userName); bdb.addPropertyValue("password", mapCustom.get(beanKey).password); bdb.addPropertyValue("timeBetweenEvictionRunsMillis", 3600000); bdb.addPropertyValue("minEvictableIdleTimeMillis", 3600000); // 注册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 ALL SM_STATIONS FROM DB1 * * @return * @throws IOException */ private Map<String, DataSourceInfo> parsePropertiesFile(String fileName) throws IOException { String sql = "SELECT STATION_ID,CENTER_DB_NAME FROM SM_STATION "; List list = jdbcTemplate.queryForList(sql); Iterator iterator = list.iterator(); Map<String, DataSourceInfo> mds = new HashMap<String, DataSourceInfo>(); while (iterator.hasNext()) { Map map4station = (Map) iterator.next(); DataSourceInfo dsi = new DataSourceInfo(); String username = "root"; String password = "root"; String url_1 = "jdbc:mysql://127.0.0.1:3306/"; String dbName = map4station.get("CENTER_DB_NAME")+""; String url_2 = "?useUnicode=true&characterEncoding=utf-8"; String key = "dataSource_"; String sid = map4station.get("STATION_ID")+""; key = key.concat(sid); String url = url_1.concat(dbName).concat(url2); dsi.connUrl = url; dsi.userName = username; dsi.password = password; mds.put(key, dsi); } return mds; } // 自定义数据结构 private class DataSourceInfo{ public String connUrl; public String userName; public String password; public String toString() { return "(url:" + connUrl + ", username:" + userName + ", password:" + password + ")"; } } }
5:如何切换
创建过滤器,得到页面传过来的切换标示,直接根据切换标示切换数据源即可;而实际后台的业务不需要做任何修改;
CustomerContextHolder.setCustomerType(beanKey);
//beanKey 就是我们创建bean 的ID 值;
DynamicDataSourceC3p0DynamicDataSourceC3p0DynamicDataSourceC3p0DynamicDataSourceC3p0
参考:http://lyunabc.iteye.com/blog/1544423 --动态切换写死在配置文件的数据源;
http://blog.csdn.net/littlechang/article/details/8071882--动态创建bean;
spring 动态创建bean 实现 多数据源切换,布布扣,bubuko.com
原文:http://blog.csdn.net/getdate/article/details/20057877