数据源连接池使用druid 其他的数据源基本原理相同
spring中配置默认数据源连接池如下:
1<!-- 数据源配置, 使用 BoneCP 数据库连接池 --> 2 <bean id="dataSourceOne" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 3 <property name="name" value="dataSourceOne"/> 4 <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass --> 5 <property name="driverClassName" value="${jdbc.driver}" /> 6 7 <!-- 基本属性 url、user、password --> 8 <property name="url" value="${jdbc.url}" /> 9 <property name="username" value="${jdbc.username}" /> 10 <property name="password" value="${jdbc.password}" /> 11 12 <!-- 配置初始化大小、最小、最大 --> 13 <property name="initialSize" value="${jdbc.pool.init}" /> 14 <property name="minIdle" value="${jdbc.pool.minIdle}" /> 15 <property name="maxActive" value="${jdbc.pool.maxActive}" /> 16 17 <!-- 配置获取连接等待超时的时间 --> 18 <property name="maxWait" value="60000" /> 19 20 <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> 21 <property name="timeBetweenEvictionRunsMillis" value="60000" /> 22 23 <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> 24 <property name="minEvictableIdleTimeMillis" value="300000" /> 25 26 <property name="validationQuery" value="${jdbc.testSql}" /> 27 <property name="testWhileIdle" value="true" /> 28 <property name="testOnBorrow" value="false" /> 29 <property name="testOnReturn" value="false" /> 30 31 <!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用) 32 <property name="poolPreparedStatements" value="true" /> 33 <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> --> 34 35 <!-- 配置监控统计拦截的filters --> 36 <property name="filters" value="stat" /> 37 </bean>
接下来配置多数据源bean
1<!-- 多数据源配置 --> 2 <bean id="dynamicDataSource" class="com.XXX.datasource.DynamicDataSource" > 3 <property name="targetDataSources"> 4 <map> 5 <entry value-ref="dataSourceOne" key="dataSourceOne"></entry> 6 7 <!--此处是对数据源的引用--> 8 <!-- <entry value-ref="dataSourceTow" key="dataSourceTow"></entry> --> 9 </map> 10 </property> 11 <property name="defaultTargetDataSource" ref="dataSourceOne" /> 12 <property name="debug" value="true"/> 13 </bean>
这个类 com.XXX.datasource.DynamicDataSource 需要手动创建
1import java.sql.Connection; 2import java.sql.DriverManager; 3import java.sql.SQLException; 4import java.util.Map; 5import java.util.Properties; 6import java.util.Set; 7 8import javax.sql.DataSource; 9 10import org.slf4j.Logger; 11import org.slf4j.LoggerFactory; 12import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 13import org.springframework.util.StringUtils; 14 15import com.alibaba.druid.pool.DruidConnectionHolder; 16import com.alibaba.druid.pool.DruidDataSource; 17import com.alibaba.druid.pool.DruidDataSourceFactory; 18import com.alibaba.druid.pool.DruidPooledConnection; 19import com.alibaba.druid.stat.DruidDataSourceStatManager; 20import com.alibaba.druid.util.DruidDataSourceUtils; 21 22/** 23 * @author zh 24 */ 25public class DynamicDataSource extends AbstractRoutingDataSource{ 26 27 private boolean debug = false; 28 Logger log = LoggerFactory.getLogger(this.getClass()); 29 private Map<Object, Object> dynamicTargetDataSources; 30 31 private Object dynamicDefaultTargetDataSource; 32 /* (non-Javadoc) 33@see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey() 34 */ 35 @Override 36 protected Object determineCurrentLookupKey() { 37 String datasource=DBContextHolder.getDataSource(); 38 if(debug) 39 { 40 if(StringUtils.isEmpty(datasource)){ 41 log.info("---当前数据源:默认数据源---"); 42 }else{ 43 log.info("---当前数据源:"+datasource+"---"); 44 } 45 } 46 47 return datasource; 48 } 49 50 @Override 51 public void setTargetDataSources(Map<Object, Object> targetDataSources) { 52 super.setTargetDataSources(targetDataSources); 53 this.dynamicTargetDataSources = targetDataSources; 54 } 55 //创建数据源 56 public boolean createDataSource(String key,String driveClass,String url,String username,String password){ 57 try { 58 try { //排除连接不上的错误 59 Class.forName(driveClass); 60 DriverManager.getConnection(url, username, password); 61 } catch (Exception e) { 62 return false; 63 } 64 @SuppressWarnings("resource") 65 DruidDataSource druidDataSource = new DruidDataSource(); 66 druidDataSource.setName(key); 67 druidDataSource.setDriverClassName(driveClass); 68 druidDataSource.setUrl(url); 69 druidDataSource.setUsername(username); 70 druidDataSource.setPassword(password); 71 druidDataSource.setMaxWait(60000); 72 druidDataSource.setFilters("stat"); 73 DataSource createDataSource = (DataSource)druidDataSource; 74 druidDataSource.init(); 75 Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources; 76 dynamicTargetDataSources2.put(key, createDataSource);//加入map 77 setTargetDataSources(dynamicTargetDataSources2);//将map赋值给父类的TargetDataSources 78 super.afterPropertiesSet();//将TargetDataSources中的连接信息放入resolvedDataSources管理 79 return true; 80 } catch (Exception e) { 81 log.error(e+""); 82 return false; 83 } 84 } 85 //删除数据源 86 public boolean delDatasources(String datasourceid){ 87 Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources; 88 if(dynamicTargetDataSources2.containsKey(datasourceid)){ 89 Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances(); 90 for(DruidDataSource l:druidDataSourceInstances){ 91 if(datasourceid.equals(l.getName())){ 92 System.out.println(l); 93 dynamicTargetDataSources2.remove(datasourceid); 94 DruidDataSourceStatManager.removeDataSource(l); 95 setTargetDataSources(dynamicTargetDataSources2);//将map赋值给父类的TargetDataSources 96 super.afterPropertiesSet();//将TargetDataSources中的连接信息放入resolvedDataSources管理 97 return true; 98 } 99 } 100 return false; 101 }else{ 102 return false; 103 } 104 } 105 106 //测试数据源连接是否有效 107 public boolean testDatasource(String key,String driveClass,String url,String username,String password){ 108 try { 109 Class.forName(driveClass); 110 DriverManager.getConnection(url, username, password); 111 return true; 112 } catch (Exception e) { 113 return false; 114 } 115 } 116 /** 117 * Specify the default target DataSource, if any. 118 * <p>The mapped value can either be a corresponding {@link javax.sql.DataSource} 119 * instance or a data source name String (to be resolved via a 120 * {@link #setDataSourceLookup DataSourceLookup}). 121 * <p>This DataSource will be used as target if none of the keyed 122 * {@link #setTargetDataSources targetDataSources} match the 123 * {@link #determineCurrentLookupKey()} current lookup key. 124 */ 125 public void setDefaultTargetDataSource(Object defaultTargetDataSource) { 126 super.setDefaultTargetDataSource(defaultTargetDataSource); 127 this.dynamicDefaultTargetDataSource = defaultTargetDataSource; 128 } 129 /** 130 * @param debug the debug to set 131 */ 132 public void setDebug(boolean debug) { 133 this.debug = debug; 134 } 135 136 /** 137 * @return the debug 138 */ 139 public boolean isDebug() { 140 return debug; 141 } 142 143 /** 144 * @return the dynamicTargetDataSources 145 */ 146 public Map<Object, Object> getDynamicTargetDataSources() { 147 return dynamicTargetDataSources; 148 } 149 150 /** 151 * @param dynamicTargetDataSources the dynamicTargetDataSources to set 152 */ 153 public void setDynamicTargetDataSources( 154 Map<Object, Object> dynamicTargetDataSources) { 155 this.dynamicTargetDataSources = dynamicTargetDataSources; 156 } 157 158 /** 159 * @return the dynamicDefaultTargetDataSource 160 */ 161 public Object getDynamicDefaultTargetDataSource() { 162 return dynamicDefaultTargetDataSource; 163 } 164 165 /** 166 * @param dynamicDefaultTargetDataSource the dynamicDefaultTargetDataSource to set 167 */ 168 public void setDynamicDefaultTargetDataSource( 169 Object dynamicDefaultTargetDataSource) { 170 this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource; 171 } 172 173}
其中该类继承了spring的AbstractRoutingDataSource 查看其源码,发现所有的数据源都是通过
afterPropertiesSet() 将存放在targetDataSources 这个Map中的数据源赋值给resolvedDataSources
对象的,spring是从resolvedDataSources对象中获取数据源对象的,我们能需要把自己的数据源放入
resolvedDataSources这个Map中就ok了。
接下来创建数据源切换工具类
1/** 2 * 数据源切换 3 * @author zh 4 * 5 */ 6public class DBContextHolder { 7 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 8 9 //调用此方法,切换数据源 10 public static void setDataSource(String dataSource) { 11 contextHolder.set(dataSource); 12 } 13 14 public static String getDataSource() { 15 return contextHolder.get(); 16 } 17 18 public static void clearDataSource() { 19 contextHolder.remove(); 20 } 21}
具体实动态新增数据源,需要创建数据库用以存储 数据库连接信息,以及数据源key信息。
初始话数据库连接数据源,可以使用spring监听 实现ApplicationListener即可,如下
1import java.util.List; 2 3import org.springframework.beans.factory.annotation.Autowired; 4import org.springframework.context.ApplicationEvent; 5import org.springframework.context.ApplicationListener; 6 7import com.casking.cdds.modules.datasource.entity.CNDatasources; 8 9public class InitDatasourcesLS implements ApplicationListener<ApplicationEvent>{ 10 11 @Autowired 12 private CNDatasourcesService service; 13 14 @Override 15 public void onApplicationEvent(ApplicationEvent event) { 16 List<CNDatasources> list = service.findList(new CNDatasources()); 17 for(CNDatasources li:list){ 18 19 //这里调用创建数据源的方法即可 20 service.addDataSourceDynamic(li.getDatasource(),li); 21 } 22 } 23 24}