前言
因为公司在给客户做的党建项目中有一个数据报送的功能,其中涉及到了数据源切换。功能全称《中组部数据报送》,涵盖的子模块:数据源管理、数据报送、报送日志。
功能的描述:把系统中的党组织信息、党员信息、行政单位等信息查出来经过处理,也就是把一些信息转换成特定的编码、代号。本来想着在配置文件中配置两个数据源,当使用数据报送模块时切换一下数据源,但是数据源是从库里面拿的,由操作者选择,所以报送的数据源是动态的。
所以在配置文件中再配一个数据源是无法实现的,因为Spring初始化后另一个数据源就是死的了,与需求不符。最后使用spring的AbstractRoutingDataSource实现数据源切换。
实现步骤
修改Spring配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="initialSize" value="${jdbc.pool.init}" /> <property name="minIdle" value="${jdbc.pool.minIdle}" /> <property name="maxActive" value="${jdbc.pool.maxActive}" /> <property name="maxWait" value="60000" /> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="${jdbc.testSql}" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" />
<property name="filters" value="stat" /> </bean> <bean id="dynamicDataSource" class="com.xxx.common.dataSource.DruidDynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="dataSource" value-ref="dataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="dataSource"/> </bean>
|
编写DBContextHolder类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import java.util.HashMap; import java.util.Map;
public class DBContextHolder { public static final String DATASOURCE_KEY = "DATASOURCE_KEY"; public static final String DATASOURCE_URL = "DATASOURCE_URL"; public static final String DATASOURCE_DRIVER = "DATASOURCE_DRIVER"; public static final String DATASOURCE_USERNAME = "DATASOURCE_USERNAME"; public static final String DATASOURCE_PASSWORD = "DATASOURCE_PASSWORD";
private static final ThreadLocal<Map<String, Object>> contextHolder = new ThreadLocal<Map<String, Object>>();
public static void setDBType(Map<String, Object> dataSourceConfigMap) { contextHolder.set(dataSourceConfigMap); }
public static Map<String, Object> getDBType() { Map<String, Object> dataSourceConfigMap = contextHolder.get(); if (dataSourceConfigMap == null) { dataSourceConfigMap = new HashMap<String, Object>(); } return dataSourceConfigMap; }
public static void clearDBType() { contextHolder.remove(); } }
|
编写AbstractDynamicDataSource类继承Spring的AbstractRoutingDataSource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public abstract class AbstractDynamicDataSource<T extends DataSource> extends AbstractRoutingDataSource implements ApplicationContextAware {
protected Logger logger = LoggerFactory.getLogger(getClass()); protected static final String DEFAULT_DATASOURCE_KEY = "dataSource";
public Map<Object, Object> targetDataSources;
private static ApplicationContext ctx;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ctx = applicationContext; }
public static ApplicationContext getApplicationContext() { return ctx; }
public static Object getBean(String name) { return ctx.getBean(name); }
public void setTargetDataSources(Map<Object, Object> targetDataSources) { this.targetDataSources = targetDataSources; super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); }
public abstract T createDataSource(String driverClassName, String url, String username, String password);
@Override protected Object determineCurrentLookupKey() { Map<String, Object> configMap = DBContextHolder.getDBType(); logger.info("【当前数据源配置为:{}】", configMap); if (MapUtils.isEmpty(configMap)) { return DEFAULT_DATASOURCE_KEY; } this.verifyAndInitDataSource(); logger.info("【切换至数据源:{}】", configMap); return configMap.get(DBContextHolder.DATASOURCE_KEY); }
private void verifyAndInitDataSource() { Map<String, Object> configMap = DBContextHolder.getDBType(); Object obj = this.targetDataSources.get(configMap.get(DBContextHolder.DATASOURCE_KEY)); if (obj != null) { return; } logger.info("【初始化数据源】"); T datasource = this.createDataSource(configMap.get(DBContextHolder.DATASOURCE_DRIVER).toString(), configMap.get(DBContextHolder.DATASOURCE_URL).toString(), configMap.get(DBContextHolder.DATASOURCE_USERNAME).toString(), configMap.get(DBContextHolder.DATASOURCE_PASSWORD).toString()); this.addTargetDataSource(configMap.get(DBContextHolder.DATASOURCE_KEY).toString(), datasource); }
private void addTargetDataSource(String key, T dataSource) { this.targetDataSources.put(key, dataSource); super.setTargetDataSources(this.targetDataSources); super.afterPropertiesSet(); }
}
|
编写DruidDynamicDataSource类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| import java.sql.SQLException; import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.druid.filter.Filter; import com.alibaba.druid.pool.DruidDataSource;
public class DruidDynamicDataSource extends AbstractDynamicDataSource<DruidDataSource> {
private boolean testWhileIdle = true; private boolean testOnBorrow = false; private boolean testOnReturn = false;
private boolean removeAbandoned = false; private long removeAbandonedTimeoutMillis = 300 * 1000; private boolean logAbandoned = false;
private String filters; private List<Filter> filterList;
@Override public DruidDataSource createDataSource(String driverClassName, String url, String username, String password) { DruidDataSource parent = (DruidDataSource) super.getApplicationContext().getBean( DEFAULT_DATASOURCE_KEY); DruidDataSource ds = new DruidDataSource(); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); ds.setDriverClassName(driverClassName); ds.setInitialSize(parent.getInitialSize()); ds.setMinIdle(parent.getMinIdle()); ds.setMaxActive(parent.getMaxActive()); ds.setMaxWait(parent.getMaxWait()); ds.setTimeBetweenConnectErrorMillis(parent.getTimeBetweenConnectErrorMillis()); ds.setTimeBetweenEvictionRunsMillis(parent.getTimeBetweenEvictionRunsMillis()); ds.setMinEvictableIdleTimeMillis(parent.getMinEvictableIdleTimeMillis());
ds.setValidationQuery(parent.getValidationQuery()); ds.setTestWhileIdle(testWhileIdle); ds.setTestOnBorrow(testOnBorrow); ds.setTestOnReturn(testOnReturn);
ds.setRemoveAbandoned(removeAbandoned); ds.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis); ds.setLogAbandoned(logAbandoned); ds.setMaxPoolPreparedStatementPerConnectionSize(parent .getMaxPoolPreparedStatementPerConnectionSize());
if (StringUtils.isNotBlank(filters)) try { ds.setFilters(filters); } catch (SQLException e) { throw new RuntimeException(e); }
addFilterList(ds); return ds; }
private void addFilterList(DruidDataSource ds) { if (filterList != null) { List<Filter> targetList = ds.getProxyFilters(); for (Filter add : filterList) { boolean found = false; for (Filter target : targetList) { if (add.getClass().equals(target.getClass())) { found = true; break; } } if (!found) targetList.add(add); } } } }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @ResponseBody @RequestMapping(value = {"dataDelivery"}) public HashMap<String, Object> dataDelivery(String id) { HashMap<String, Object> result = new HashMap<String, Object>(); List<ReturnMessage> list = new ArrayList<ReturnMessage>(); try { DeliveryDataSources dataSources = deliveryDataSourcesService.get(id); if(ConnectionTest.getConnection(dataSources)) { DataList dataList =findDataService.findData(); String key = "DataSources"+System.identityHashCode(d); Map<String, Object> map = new HashMap<String, Object>(); map.put(DBContextHolder.DATASOURCE_KEY, key); map.put(DBContextHolder.DATASOURCE_DRIVER, dataSources.getDatabaseDriver()); map.put(DBContextHolder.DATASOURCE_URL,dataSources.getUrl()); map.put(DBContextHolder.DATASOURCE_USERNAME, dataSources.getDatabaseUsername()); map.put(DBContextHolder.DATASOURCE_PASSWORD,dataSources.getDatabasePassword()); DBContextHolder.setDBType(map); list = dataDeliveryService.batchSaveDataList(dataList); DBContextHolder.clearDBType(); result.put("status", "1"); result.put("list", list); }else { result.put("status", "2"); } } catch (Exception e) { result.put("status", "3"); System.out.println("报送失败"); e.printStackTrace(); } return result; }
|
效果(其实通过图片看不出来什么)


