問題描述:
項目一直是使用同一個數(shù)據(jù)庫進行讀和寫的操作盒蟆,在寫操作時會鎖表飞醉,并且效率低痕惋,后期出現(xiàn)性能問題
原因描述:
項目只能操作一個數(shù)據(jù)源
需求描述:
配置多個數(shù)據(jù)源,利用切面進行動態(tài)切換
解決方案:
修改配置泌豆,并且增加切面進行自動控制
解決方式:
第一步:配置多個數(shù)據(jù)源:
?數(shù)據(jù)源1
<!-- 數(shù)據(jù)源 -->
<bean id="DataSource1" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="1000" />
<property name="initialSize" value="1" />
<property name="maxWait" value="80000" />
<property name="minIdle" value="1" />
<!-- 超過時間限制是否回收 -->
<property name="removeAbandoned" value="true" />
<!-- 超時時間;單位為秒吏饿。120秒=2分鐘 -->
<property name="removeAbandonedTimeout" value="120" />
<!-- 關(guān)閉abanded連接時輸出錯誤日志 -->
<!-- <property name="logAbandoned" value="true" /> -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="defaultAutoCommit" value="true" />
</bean>
數(shù)據(jù)源2
<!-- 數(shù)據(jù)源 -->
<bean id="DataSource2" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url2}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="1000" />
<property name="initialSize" value="1" />
<property name="maxWait" value="80000" />
<property name="minIdle" value="1" />
<!-- 超過時間限制是否回收 -->
<property name="removeAbandoned" value="true" />
<!-- 超時時間踪危;單位為秒。120秒=2分鐘 -->
<property name="removeAbandonedTimeout" value="120" />
<!-- 關(guān)閉abanded連接時輸出錯誤日志 -->
<!-- <property name="logAbandoned" value="true" /> -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="defaultAutoCommit" value="true" />
</bean>
第二步:配置選擇數(shù)據(jù)源的bean
a.配置數(shù)據(jù)源切換的bean
<bean id="dataSource" class="com.zsrt.devbase.datesource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="DataSource1" key="testDataSource1"></entry>
<entry value-ref="DataSource2" key="testDataSource2"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="testDataSource1" />
</bean>
?class 為自定義的數(shù)據(jù)源切換控制的bean
在map中猪落,key指向的是后臺需要選擇的數(shù)據(jù)源名稱贞远,value指向的是第一步配置的數(shù)據(jù)源id
b.編寫切換控制器
/**
* 數(shù)據(jù)源的切換與選擇器
* @author leixin
* @Date 2017年4月19日
* @version 1.0
*/
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* @Description: 設(shè)置數(shù)據(jù)源類型
* @param dataSourceType 數(shù)據(jù)庫類型
* @return void
* @throws
*/
public static void setDataSourceType(String dataSourceType) {
System.err.println(dataSourceType);
contextHolder.set(dataSourceType);
}
/**
* @Description: 獲取數(shù)據(jù)源類型
* @param
* @return String
* @throws
*/
public static String getDataSourceType() {
return contextHolder.get();
}
/**
* @Description: 清除數(shù)據(jù)源類型
* @param
* @return void
* @throws
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
c.編寫切換控制器
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
以上步驟已經(jīng)完成數(shù)據(jù)庫的手動切換,在控制層可以手動切換笨忌。
具體切換方法為:當(dāng)需要調(diào)用數(shù)據(jù)源A時蓝仲,用靜態(tài)的類去設(shè)置數(shù)據(jù)源A的名字,然后再去調(diào)用service
問題:每次都需要手動切換官疲,有點麻煩袱结,因此想到利用切面控制
在上面的基礎(chǔ)上利用切面控制,最先想到的是切面控制service途凫,但是控制service會和事務(wù)控制切面產(chǎn)生沖突(原因是事務(wù)需要鎖定數(shù)據(jù)源的鏈接垢夹,而當(dāng)前的數(shù)據(jù)源鏈接還沒有進行動態(tài)切換,所以只能選擇默認(rèn)的數(shù)據(jù)源维费,從而導(dǎo)致切換失敼)
思路一:
于是想到切面切入點為控制層,在開啟事務(wù)之前就切換數(shù)據(jù)源犀盟。
思路二:
????? 利用@order注解控制切面的執(zhí)行順序
經(jīng)過上面的分析而晒,思路二明顯優(yōu)于思路一,于是開始嘗試思路二且蓬,在思路二的基礎(chǔ)上欣硼,打算開啟注解切入
第一步:自定義切面注解: ? ??
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
}
第二步:編寫切面:
@Aspect
@Order(0)
public class DataSourceAspect{
@AfterReturning("execution(* com.zsrt.devbase.service.*.*(..))")
public void afterReturning() throws Throwable {
DataSourceContextHolder.clearDataSourceType();
}
@Before("execution(* com.zsrt.devbase.service.*.*(..)) && @annotation(com.zsrt.devbase.annotation.DataSource)")
public void before(JoinPoint jp) throws Throwable {
DataSourceContextHolder.setDataSourceType("testDataSource2");
}
}
第三步:配置事務(wù)切面的執(zhí)行順序:
<aop:config>
<!-- 切點表達式:所有的service類中的方法都會有事務(wù) -->
<aop:pointcut id="appService"
expression="execution(* com.zsrt.devbase.service.*Service*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="appService" order="1" />
</aop:config>
結(jié)果:
經(jīng)測試,程序按照預(yù)期進行執(zhí)行:
先切換了數(shù)據(jù)源,然后開啟了事務(wù)诈胜,最后再執(zhí)行了別的切面
---------------------
原文:https://blog.csdn.net/leixin821792669/article/details/70242397