spring实现动态添加数据源

动态添加数据源

在开发中,总会有一些业务需要从第三方数据源获取数据,因此需要给用户提供动态添加数据源的功能

通过在网上查阅资料发现spring有一个抽象类AbstractRoutingDataSource,通过实现这个类中的抽象方法determineCurrentLookupKey()可以判断使用的是哪一个数据源。

在AbstractRoutingDataSource类中,可以通过determineTargetDataSource()方法获取数据源,在该方法里又通过determineCurrentLookupKey()方法判断使用哪一个数据源,通过获得的key获取在map中存取的数据源信息。

因为,实现动态数据源时有两种思路:

  1. 维护一个数据源map,并且重写determineTargetDataSource()方法,从自己维护的map中取出对应的数据源信息;
  2. 修改AbstractRoutingDataSource类的resolvedDataSources属性,将数据源信息动态地添加进去,但是由于该属性是私有的,只能通过反射来实现。

比较之后,选择的第一种思路,DynamicDataSource类的代码如下:

public class DynamicDataSource extends AbstractRoutingDataSource {private static Map dataSourceMap = new HashMap<>();private static Map dseMap = new HashMap<>();private static DataSource defaultDataSource;public void setDataSourceMap(Map dataSourceMap) {DynamicDataSource.dataSourceMap = dataSourceMap;}public static DataSource getDefaultDataSource() {return defaultDataSource;}public void setDefaultDataSource(DataSource defaultDataSource) {DynamicDataSource.defaultDataSource = defaultDataSource;}@Overrideprotected String determineCurrentLookupKey() {return DataSourceUtils.getDbKey();}@Overridepublic void afterPropertiesSet() {}/*** 设置使用哪个数据源** @return*/@Overridepublic DataSource determineTargetDataSource() {String key = determineCurrentLookupKey();log.info("当前数据源为"+key);DataSource dataSource = dataSourceMap.get(key);if (dataSource == null) {dataSource = defaultDataSource;}return dataSource;}public static boolean checkDbKey(String dbKey) {if (dataSourceMap.get(dbKey) != null) {return true;}return false;}/*** 添加数据源** @param dse* @return*/public synchronized String addDataSource(DataSourceEntity dse) {if (dseMap.get(dse) != null) {// 如果dsemap中有,datasourcemap中没有/*if(!dataSourceMap.containsKey(dseMap.get(dse))){dataSourceMap.put(dse.getDatasourceKey(), createDateSource(dse));}*/return dseMap.get(dse);}DataSource ds = createDateSource(dse);// 使用dse的name作为keydataSourceMap.put(dse.getDatasourceKey(), ds);dseMap.put(dse, dse.getDatasourceKey());return dse.getDatasourceKey();}/*** 创建数据源** @param dsInfo* @return*/private DataSource createDateSource(DataSourceEntity dsInfo) {BasicDataSource ds = new BasicDataSource();if (dsInfo.getType() == 1) {ds.setDriverClassName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://" + dsInfo.getIp() + ":" + dsInfo.getPort() + "/" + dsInfo.getDatabaseName();ds.setUrl(url);} else if (dsInfo.getType() == 2) {ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");String url = "jdbc:oracle://" + dsInfo.getIp() + ":" + dsInfo.getPort() + "/" + dsInfo.getDatabaseName();ds.setUrl(url);}ds.setUsername(dsInfo.getUserName());ds.setPassword(dsInfo.getPassword());// 设置连接池属性ds.setInitialSize(1);ds.setMinIdle(1);ds.setTestWhileIdle(true);ds.setTestOnBorrow(false);ds.setTestOnReturn(false);ds.setValidationQuery("select 1");ds.setValidationQueryTimeout(180);ds.setTimeBetweenEvictionRunsMillis(6000);ds.setNumTestsPerEvictionRun(8);return ds;}/*** 测试是否联通* @param dbkey* @return*/public boolean testConn(String dbkey){DataSourceUtils.setDbKey(dbkey);try {Connection connection = determineTargetDataSource().getConnection();if (connection != null) {return true;}} catch (SQLException throwables) {throwables.printStackTrace();}finally {DataSourceUtils.setDbKey("defaultDataSource");}return false;}/*** 返回DataSourceMap* @return*/public static Map getDataSourceMap() {return dataSourceMap;}public static Map getDseMap() {return dseMap;}public static void remove(String dbKey){dataSourceMap.remove(dbKey);dseMap.remove(dbKey);}
}

DataSourceUtils的代码如下:

@Log4j2
public class DataSourceUtils {private static final ThreadLocal local = new ThreadLocal();public static String getDbKey() {return local.get();}public static void setDbKey(String dbKey) {if (DynamicDataSource.checkDbKey(dbKey)) {log.info("切换为:" + dbKey);local.set(dbKey);} else {throw new NullPointerException("不存在名称为" + dbKey + "的数据源");}}public static void clearDbkey() {local.remove();}}

在DynamicDataSource类的createDateSource方法中,我们手动设置了连接池的属性,并定期检查新建数据源连接是否生效。

需要注意的是,如果新建数据源的使用的连接池最好与项目默认数据源的一致。如果不一致的话,可能回导致数据源连接在一段空闲时间后会自动断开。

比如:如果项目中使用的是DBCP2连接池,那么在createDateSource中新建数据源时应该导入dpcp2中的类BasicDataSource


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部