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 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
| <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/>
<property name="initialSize" value="5"/> <property name="maxActive" value="10"/> <property name="minIdle" value="1"/> <property name="maxWait" value="3000"/>
<property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="33"/>
<property name="validationQuery" value="SELECT 1"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="removeAbandoned" value="false"/> </bean>
<bean name="adDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${adurl}"/> <property name="username" value="${adusername}"/> <property name="password" value="${adpassword}"/>
<property name="initialSize" value="5"/> <property name="maxActive" value="10"/> <property name="minIdle" value="1"/> <property name="maxWait" value="3000"/>
<property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="33"/>
<property name="validationQuery" value="SELECT 1"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="removeAbandoned" value="false"/> </bean>
<bean id="dynamicDataSource" class="com.zhanghe.webconfig.datasource.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="video" value-ref="dataSource"/> <entry key="ad" value-ref="adDataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="dataSource"/> </bean>
|
动态数据源
配置好了数据源之后,需要进行定义动态数据源,继承AbstractRoutingDataSource,AbstractRoutingDataSource是基于特定的查找key路由到特定的数据源。它内部维护了一组目标数据源,并且做了路由key与目标数据源之间的映射,提供基于key查找数据源的方法。
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
| public class DynamicDataSource extends AbstractRoutingDataSource { private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
@Override protected Object determineCurrentLookupKey() { String key = DataSourceHolder.getCurDataSource(); LOGGER.info("{}线程 获取到的数据源key--->{}",Thread.currentThread().getName(),key); if(StringUtils.isBlank(key)){ key = DataSourceHolder.getDefaultDataSource(); } LOGGER.info("{}线程 数据源选择--->{}",Thread.currentThread().getName(),key); return key; } }
public class DataSourceHolder { private static final ThreadLocal<String> CUR_DATA_SOURCE = new ThreadLocal<>();
private static final String DEFAULT_DATA_SOURCE = "video";
public static String getCurDataSource(){ return CUR_DATA_SOURCE.get(); }
public static String getDefaultDataSource(){ return DEFAULT_DATA_SOURCE; }
public static void setCurDataSource(String dataSource){ if(StringUtils.isNotBlank(dataSource)){ CUR_DATA_SOURCE.set(dataSource); } }
public static void clearDataSource(){ CUR_DATA_SOURCE.remove(); } }
|
配置是都搞定了,那怎么切换呢,可以看到在动态数据源中其实是根据key来进行路由获取数据源的,其实就是怎么改变这个key,而且是动态改变,就用spring aop来进行解决吧
数据源切换
首先定义一个注解@DataSource,来标识当前方法要使用的数据源
1 2 3 4 5 6
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource {
String name() default "video"; }
|
然后来进行aop的逻辑来根据注解的name属性来存储key
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
| @Order(1) @Aspect @Component public class DataSourceAspect { private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
@Pointcut(value="@annotation(com.zhanghe.webconfig.datasource.DataSource)") public void pointcut(){
}
@Before(value = "pointcut()") public void before(JoinPoint joinPoint){ String name = getDataSourceName(joinPoint); LOGGER.info("{}线程拦截切换数据源{}",Thread.currentThread().getName(),name); DataSourceHolder.setCurDataSource(name); }
@After(value = "pointcut()") public void after(){ DataSourceHolder.clearDataSource(); }
public String getDataSourceName(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); if (method != null) { DataSource dataSource = method.getAnnotation(DataSource.class);
return dataSource.name(); } else { return null; } } }
|
这就大功告成了,双数据源的配置就搞定了
使用
在使用的时候在方法上配置对应的数据源即可,注意需要新开事务
1 2 3
| @Transactional(propagation = Propagation.REQUIRES_NEW) @DataSource(name = "ad") public void deleteExpireData() {
|