應用場景:在一個項目需要用到兩個或兩個以上的數據庫時,要進行切換數據庫,來操作相應的表。
框架:用的是spring 的org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 這個類 實現determineCurrentLookupKey() 方法來切換數據源
1. 配置文件,配置多個數據源。
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="${datasource.driverClassName}" /> <property name="url" value="${datasource.url}" /> <property name="username" value="${datasource.username}" /> <property name="password" value="${datasource.password}" /> <property name="filters" value="stat" /> </bean> <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="${datasource2.driverClassName}" /> <property name="url" value="${datasource2.url}" /> <property name="username" value="${datasource2.username}" /> <property name="password" value="${datasource2.password}" /> <property name="maxWait" value="${datasource2.maxWait}"/> <property name="filters" value="stat" /> </bean> <bean id="multipleDataSource" class="com.ds.basic.util.MultipleDataSource"> <property name="defaultTargetDataSource" ref="dataSource1"/> <property name="targetDataSources"> <map> <entry key="dataSource1" value-ref="dataSource1"/> <entry key="dataSource2" value-ref="dataSource2"/> </map> </property> </bean>
多了個multipleDataSource 來把多個數據源組合在一起,其他配置記得ref="multipleDataSource" 這個切換到這個來。
還有事務管理器,
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="2"/>
order = '2',增加個這個,下面會用到。
添加aop 依賴包,
<!-- 配置自動掃描的包 --> <context:component-scan base-package="com.ds.basic.aop"></context:component-scan> <!-- 自動為切面方法中匹配的方法所在的類生成代理對象。 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
截止,配置文件完成。
com.ds.basic.util.MultipleDataSource ,切換數據源核心,就是要實現determineCurrentLookupKey ,來切換數據源
public class MultipleDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>(); public static void setDataSourceKey(String dataSource) { dataSourceKey.set(dataSource); } @Override protected Object determineCurrentLookupKey() { return dataSourceKey.get(); } }
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { /* 自定義個注解來做個標識,好切換數據源 */ String value(); }
/**
* order(1) 是為了要在事務之前就切換數據源,不然應該會有問題,大家可以試一試
*/
@Order(1) @Component @Aspect public class DataSourceAspect { /** * 攔截,獲取由@DataSource指定的數據源標識,設置到線程存儲中以便切換數據源 * * @param point * @throws Exception * @annotation使用這個表達式來切,卻一直沒起作用,有大佬知道請評論告知。 */ // @Before("@annotation(com.ds.basic.util.DataSource)") @Before(value="execution(* com.shop.*.*.service.*.*(..))") public void intercept(JoinPoint point) throws Exception { // 下面的注釋是在生產環境下切到第二個數據源時總是不能切回來,加個這個就好了,很奇怪,其實沒必要添加這個。 // MultipleDataSource.setDataSourceKey("dataSource1"); Class<?> target = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); // 默認使用目標類型的注解,如果沒有則使用其實現接口的注解 for (Class<?> clazz : target.getInterfaces()) { resolveDataSource(clazz, signature.getMethod()); } resolveDataSource(target, signature.getMethod()); } /** * 提取目標對象方法注解和類型注解中的數據源標識 * 這個方法是在一個博客上復制下來的,原來的網址不記得在哪里了 * @param clazz * @param method */ private void resolveDataSource(Class<?> clazz, Method method) { try { Class<?>[] types = method.getParameterTypes(); // 默認使用類型注解 if (clazz.isAnnotationPresent(DataSource.class)) { DataSource source = clazz.getAnnotation(DataSource.class); MultipleDataSource.setDataSourceKey(source.value()); } // 方法注解可以覆蓋類型注解 Method m = clazz.getMethod(method.getName(), types); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource source = m.getAnnotation(DataSource.class); MultipleDataSource.setDataSourceKey(source.value()); } } catch (Exception e) { // 可以不做這個異常捕捉。 MultipleDataSource.setDataSourceKey("dataSource1"); } } }
配置完成,大家可以試下,歡迎評論。
使用 @DataSource("dataSource2") 在service 上加上這個就能切換了,沒有加,默認是第一個的,
看了網上很多方法 比如用做兩個事務管理器來指定不同的數據源,這樣在切換事務管理器時就切換了數據源,但是我的配置只是連上了,卻無法切換 一直用的還是第一個數據源。
