




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
第使用注解解決ShardingJdbc不支持復(fù)雜SQL方法目錄背景介紹問題復(fù)現(xiàn)解決思路具體實現(xiàn)編寫autoConfig類自定義數(shù)據(jù)源aop攔截器:aop切面定義ThreadLocal啟動類配置
背景介紹
公司最近做分庫分表業(yè)務(wù),接入了ShardingJDBC,接入完成后,回歸測試時發(fā)現(xiàn)好幾個SQL執(zhí)行報錯,關(guān)鍵這幾個表都還不是分片表。
報錯如下:
這下糟了嘛。熟悉ShardingJDBC的同學(xué)應(yīng)該知道,有很多SQL它是不支持的。官方截圖如下:
如果要去修改這些復(fù)雜SQL的話,可能要花費很多時間。那怎么辦呢?只能從ShardingJDBC這里找突破口了,兩天的研究,出來了下面這個只需要加一個注解輕松解決ShardingJdbc不支持復(fù)雜SQL的方案。
問題復(fù)現(xiàn)
我本地寫了一個復(fù)雜SQL進行測試:
publicListMapString,ObjectqueryOrder(){
ListMapString,Objectorders=borderRepository.findOrders();
returnorders;
publicinterfaceBOrderRepositoryextendsJpaRepositoryBOrder,Long{
@Query(value="SELECT*FROM(SELECTid,CASEWHENcompany_id=1THEN'小'WHENcompany_id=4THEN'中'ELSE'大'ENDAScom,user_idasuserIdFROMb_order0)tWHERE='中'",nativeQuery=true)
ListMapString,ObjectfindOrders();
寫了個測試controller來調(diào)用,調(diào)用后果然報錯了。
解決思路
因為查詢的復(fù)雜SQL的表不是分片表,那能不能指定這幾個復(fù)雜查詢的時候不用ShardingJDBC的數(shù)據(jù)源呢?
在注入ShardingJDBC數(shù)據(jù)源的地方做處理,注入一個我們自定義的數(shù)據(jù)源這樣我們獲取連接的時候就能返回原生數(shù)據(jù)源了另外我們聲明一個注解,對標(biāo)識了注解的就返回原生數(shù)據(jù)源,否則還是返回Sharding數(shù)據(jù)源
具體實現(xiàn)
編寫autoConfig類
編寫一個autoConfig類,來替換ShardingSphereAutoConfiguration類
/**
*動態(tài)數(shù)據(jù)源核心自動配置類
@Configuration
@ComponentScan("org.apache.shardingsphere.spring.boot.converter")
@EnableConfigurationProperties(SpringBootPropertiesConfiguration.class)
@ConditionalOnProperty(prefix="spring.shardingsphere",name="enabled",havingValue="true",matchIfMissing=true)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
publicclassDynamicDataSourceAutoConfigurationimplementsEnvironmentAware{
privateStringdatabaseName;
privatefinalSpringBootPropertiesConfigurationprops;
privatefinalMapString,DataSourcedataSourceMap=newLinkedHashMap();
publicDynamicDataSourceAutoConfiguration(SpringBootPropertiesConfigurationprops){
ps=props;
*Getmodeconfiguration.
*@returnmodeconfiguration
@Bean
publicModeConfigurationmodeConfiguration(){
returnnull==props.getMode()null:newModeConfigurationYamlSwapper().swapToObject(props.getMode());
*GetShardingSpheredatasourcebean.
*@paramrulesrulesconfiguration
*@parammodeConfigmodeconfiguration
*@returndatasourcebean
*@throwsSQLExceptionSQLexception
@Bean
@Conditional(LocalRulesCondition.class)
@Autowired(required=false)
publicDataSourceshardingSphereDataSource(finalObjectProviderListRuleConfigurationrules,finalObjectProviderModeConfigurationmodeConfig)throwsSQLException{
CollectionRuleConfigurationruleConfigs=Optional.ofNullable(rules.getIfAvailable()).orElseGet(Collections::emptyList);
DataSourcedataSource=ShardingSphereDataSourceFactory.createDataSource(databaseName,modeConfig.getIfAvailable(),dataSourceMap,ruleConfigs,props.getProps());
returnnewWrapShardingDataSource((ShardingSphereDataSource)dataSource,dataSourceMap);
*Getdatasourcebeanfromregistrycenter.
*@parammodeConfigmodeconfiguration
*@returndatasourcebean
*@throwsSQLExceptionSQLexception
@Bean
@ConditionalOnMissingBean(DataSource.class)
publicDataSourcedataSource(finalModeConfigurationmodeConfig)throwsSQLException{
DataSourcedataSource=!dataSourceMap.isEmpty()ShardingSphereDataSourceFactory.createDataSource(databaseName,modeConfig,dataSourceMap,Collections.emptyList(),props.getProps())
:ShardingSphereDataSourceFactory.createDataSource(databaseName,modeConfig);
returnnewWrapShardingDataSource((ShardingSphereDataSource)dataSource,dataSourceMap);
*Createtransactiontypescanner.
*@returntransactiontypescanner
@Bean
publicTransactionTypeScannertransactionTypeScanner(){
returnnewTransactionTypeScanner();
@Override
publicfinalvoidsetEnvironment(finalEnvironmentenvironment){
dataSourceMap.putAll(DataSourceMapSetter.getDataSourceMap(environment));
databaseName=DatabaseNameSetter.getDatabaseName(environment);
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
@ConditionalOnProperty(prefix="spring.datasource.dynamic.aop",name="enabled",havingValue="true",matchIfMissing=true)
publicAdvisordynamicDatasourceAnnotationAdvisor(){
DynamicDataSourceAnnotationInterceptorinterceptor=newDynamicDataSourceAnnotationInterceptor(true);
DynamicDataSourceAnnotationAdvisoradvisor=newDynamicDataSourceAnnotationAdvisor(interceptor,DS.class);
returnadvisor;
自定義數(shù)據(jù)源
publicclassWrapShardingDataSourceextendsAbstractDataSourceAdapterimplementsAutoCloseable{
privateShardingSphereDataSourcedataSource;
privateMapString,DataSourcedataSourceMap;
publicWrapShardingDataSource(ShardingSphereDataSourcedataSource,MapString,DataSourcedataSourceMap){
this.dataSource=dataSource;
this.dataSourceMap=dataSourceMap;
publicDataSourcegetTargetDataSource(){
Stringpeek=DynamicDataSourceContextHolder.peek();
if(StringUtils.isEmpty(peek)){
returndataSource;
returndataSourceMap.get(peek);
@Override
publicConnectiongetConnection()throwsSQLException{
returngetTargetDataSource().getConnection();
@Override
publicConnectiongetConnection(finalStringusername,finalStringpassword)throwsSQLException{
returngetConnection();
@Override
publicvoidclose()throwsException{
DataSourcetargetDataSource=getTargetDataSource();
if(targetDataSourceinstanceofAutoCloseable){
((AutoCloseable)targetDataSource).close();
@Override
publicintgetLoginTimeout()throwsSQLException{
DataSourcetargetDataSource=getTargetDataSource();
returntargetDataSource==null0:targetDataSource.getLoginTimeout();
@Override
publicvoidsetLoginTimeout(finalintseconds)throwsSQLException{
DataSourcetargetDataSource=getTargetDataSource();
targetDataSource.setLoginTimeout(seconds);
聲明指定數(shù)據(jù)源注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceDS{
*數(shù)據(jù)源名
Stringvalue();
另外使用AOP的方式攔截使用了注解的類或方法,并且要將這些用了注解的方法存起來,在獲取數(shù)據(jù)源連接的時候取出來進行判斷。這就還要用到ThreadLocal。
aop攔截器:
publicclassDynamicDataSourceAnnotationInterceptorimplementsMethodInterceptor{
privatefinalDataSourceClassResolverdataSourceClassResolver;
publicDynamicDataSourceAnnotationInterceptor(BooleanallowedPublicOnly){
dataSourceClassResolver=newDataSourceClassResolver(allowedPublicOnly);
@Override
publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
StringdsKey=determineDatasourceKey(invocation);
DynamicDataSourceContextHolder.push(dsKey);
try{
returnceed();
}finally{
DynamicDataSourceContextHolder.poll();
privateStringdetermineDatasourceKey(MethodInvocationinvocation){
Stringkey=dataSourceClassResolver.findKey(invocation.getMethod(),invocation.getThis());
returnkey;
aop切面定義
/**
*aopAdvisor
publicclassDynamicDataSourceAnnotationAdvisorextendsAbstractPointcutAdvisorimplementsBeanFactoryAware{
privatefinalAdviceadvice;
privatefinalPointcutpointcut;
privatefinalClassextendsAnnotationannotation;
publicDynamicDataSourceAnnotationAdvisor(MethodInterceptoradvice,
ClassextendsAnnotationannotation){
this.advice=advice;
this.annotation=annotation;
this.pointcut=buildPointcut();
@Override
publicPointcutgetPointcut(){
returnthis.pointcut;
@Override
publicAdvicegetAdvice(){
returnthis.advice;
@Override
publicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{
if(this.adviceinstanceofBeanFactoryAware){
((BeanFactoryAware)this.advice).setBeanFactory(beanFactory);
privatePointcutbuildPointcut(){
Pointcutcpc=newAnnotationMatchingPointcut(annotation,true);
Pointcutmpc=newAnnotationMethodPoint(annotation);
returnnewComposablePointcut(cpc).union(mpc);
*Inordertobecompatiblewiththespringlowerthan5.0
privatestaticclassAnnotationMethodPointimplementsPointcut{
privatefinalClassextendsAnnotationannotationType;
publicAnnotationMethodPoint(ClassextendsAnnotationannotationType){
Assert.notNull(annotationType,"Annotationtypemustnotbenull");
this.annotationType=annotationType;
@Override
publicClassFiltergetClassFilter(){
returnClassFilter.TRUE;
@Override
publicMethodMatchergetMethodMatcher(){
returnnewAnnotationMethodMatcher(annotationType);
privatestaticclassAnnotationMethodMatcherextendsStaticMethodMatcher{
privatefinalClassextendsAnnotationannotationType;
publicAnnotationMethodMatcher(ClassextendsAnnotationannotationType){
this.annotationType=annotationType;
@Override
publicbooleanmatches(Methodmethod,ClasstargetClass){
if(matchesMethod(method)){
returntrue;
//Proxyclassesneverhaveannotationsontheirredeclaredmethods.
if(Proxy.isProxyClass(targetClass)){
returnfalse;
//Themethodmaybeonaninterface,solet'scheckonthetargetclassaswell.
MethodspecificMethod=AopUtils.getMostSpecificMethod(method,targetClass);
return(specificMethod!=methodmatchesMethod(specificMethod));
privatebooleanmatchesMethod(Methodmethod){
returnAnnotatedElementUtils.hasAnnotation(method,this.annotationType);
/**
*數(shù)據(jù)源解析器
publicclassDataSourceClassResolver{
privatestaticbooleanmpEnabled=false;
privatestaticFieldmapperInterfaceField;
static{
ClassproxyClass=null;
try{
proxyClass=Class.forName("com.baomidou.mybatisplus.core.override.MybatisMapperProxy");
}catch(ClassNotFoundExceptione1){
try{
proxyClass=Class.forName("com.baomidou.mybatisplus.core.override.PageMapperProxy");
}catch(ClassNotFoundExceptione2){
try{
proxyClass=Class.forName("org.apache.ibatis.binding.MapperProxy");
}catch(ClassNotFoundExceptionignored){
if(proxyClass!=null){
try{
mapperInterfaceField=proxyClass.getDeclaredField("mapperInterface");
mapperInterfaceField.setAccessible(true);
mpEnabled=true;
}catch(NoSuchFieldExceptione){
e.printStackTrace();
*緩存方法對應(yīng)的數(shù)據(jù)源
privatefinalMapObject,StringdsCache=newConcurrentHashMap();
privatefinalbooleanallowedPublicOnly;
*加入擴展,給外部一個修改aop條件的機會
*@paramallowedPublicOnly只允許公共的方法,默認(rèn)為true
publicDataSourceClassResolver(booleanallowedPublicOnly){
this.allowedPublicOnly=allowedPublicOnly;
*從緩存獲取數(shù)據(jù)
*@parammethod方法
*@paramtargetObject目標(biāo)對象
*@returnds
publicStringfindKey(Methodmethod,ObjecttargetObject){
if(method.getDeclaringClass()==Object.class){
return"";
ObjectcacheKey=newMethodClassKey(method,targetObject.getClass());
Stringds=this.dsCache.get(cacheKey);
if(ds==null){
ds=computeDatasource(method,targetObject);
if(ds==null){
ds="";
this.dsCache.put(cacheKey,ds);
returnds;
*查找注解的順序
*1.當(dāng)前方法
*2.橋接方法
*3.當(dāng)前類開始一直找到Object
*4.支持mybatis-plus,mybatis-spring
*@parammethod方法
*@paramtargetObject目標(biāo)對象
*@returnds
privateStringcomputeDatasource(Methodmethod,ObjecttargetObject){
if(allowedPublicOnly!Modifier.isPublic(method.getModifiers())){
returnnull;
//1.從當(dāng)前方法接口中獲取
StringdsAttr=findDataSourceAttribute(method);
if(dsAttr!=null){
returndsAttr;
ClasstargetClass=targetObject.getClass();
ClassuserClass=ClassUtils.getUserClass(targetClass);
//JDK代理時,獲取實現(xiàn)類的方法聲明.method:接口的方法,specificMethod:實現(xiàn)類方法
MethodspecificMethod=ClassUtils.getMostSpecificMethod(method,userClass);
specificMethod=BridgeMethodResolver.findBridgedMethod(specificMethod);
//2.從橋接方法查找
dsAttr=findDataSourceAttribute(specificMethod);
if(dsAttr!=null){
returndsAttr;
//從當(dāng)前方法聲明的類查找
dsAttr=findDataSourceAttribute(userClass);
if(dsAttr!=nullClassUtils.isUserLevelMethod(method)){
returndsAttr;
//since3.4.1從接口查找,只取第一個找到的
for(ClassinterfaceClazz:ClassUtils.getAllInterfacesForClassAsSet(userClass)){
dsAttr=findDataSourceAttribute(interfaceClazz);
if(dsAttr!=null){
returndsAttr;
//如果存在橋接方法
if(specificMethod!=method){
//從橋接方法查找
dsAttr=findDataSourceAttribute(method);
if(dsAttr!=null){
returndsAttr;
//從橋接方法聲明的類查找
dsAttr=findDataSourceAttribute(method.getDeclaringClass());
if(dsAttr!=nullClassUtils.isUserLevelMethod(method)){
returndsAttr;
returngetDefaultDataSourceAttr(targetObject);
*默認(rèn)的獲取數(shù)據(jù)源名稱方式
*@paramtargetObject目標(biāo)對象
*@returnds
privateStringgetDefaultDataSourceAttr(ObjecttargetObject){
ClasstargetClass=targetObject.getClass();
//如果不是代理類,從當(dāng)前類開始,不斷的找父類的聲明
if(!Proxy.isProxyClass(targetClass)){
ClasscurrentClass=targetClass;
while(currentClass!=Object.class){
StringdatasourceAttr=findDataSourceAttribute(currentClass);
if(datasourceAttr!=null){
returndatasourceAttr;
currentClass=currentClass.getSuperclass();
//mybatis-plus,mybatis-spring的獲取方式
if(mpEnabled){
finalClassclazz=getMapperInterfaceClass(targetObject);
if(clazz!=null){
StringdatasourceAttr=findDataSourceAttribute(clazz);
if(datasourceAttr!=null){
returndatasourceAttr;
//嘗試從其父接口獲取
returnfindDataSourceAttribute(clazz.getSuperclass());
returnnull;
*用于處理嵌套代理
*@paramtargetJDK代理類對象
*@returnInvocationHandler的Class
privateClassgetMapperInterfaceClass(Objecttarget){
Objectcurrent=target;
while(Proxy.isProxyClass(current.getClass())){
ObjectcurrentRefObject=AopProxyUtils.getSingletonTarget(current);
if(currentRefObject==null){
break;
current=currentRefObject;
try{
if(Proxy.isProxyClass(current.getClass())){
return(Class)mapperInterfaceField.get(Proxy.getInvocationHandler(current));
}catch(IllegalAccessExceptionignore){
returnnull;
*通過AnnotatedElement查找標(biāo)記的注解,映射為DatasourceHolder
*@paramaeAnnotatedElement
*@return數(shù)據(jù)源映射持有者
privateStringfindDataSourceAttribute(AnnotatedElementae){
AnnotationAttributesattributes=AnnotatedElementUtils.getMergedAnnotationAttributes(ae,DS.class);
if(attributes!=null){
returnattributes.getString("value");
returnnull;
ThreadLocal
publicfinalclassDynamicDataSourceContextHolder{
*為什么要用鏈表存儲(準(zhǔn)確的是棧)
*pre
*為了支持嵌套切換,如ABC三個service都是不同的數(shù)據(jù)源
*其中A的某個業(yè)務(wù)要調(diào)B的方法,B的方法需要調(diào)用C的方法。一級一級調(diào)用切換,形成了鏈。
*傳統(tǒng)的只設(shè)置當(dāng)前線程的方式不能滿足此業(yè)務(wù)需求,必須使用棧,后進先出。
*/pre
privatestaticfinalThreadLocalDequeStringLOOKUP_KEY_HOLDER=newNamedThreadLocalDequeSt
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 項目結(jié)果評估的關(guān)鍵指標(biāo)試題及答案
- 基于大數(shù)據(jù)分析的2025年電動汽車電池?zé)峁芾砑夹g(shù)故障診斷報告
- 2025年濕式碾米機合作協(xié)議書
- 2025年建筑施工安全管理信息化對施工現(xiàn)場安全文化的培育報告
- 行政改革與公共政策創(chuàng)新試題及答案
- 智慧校園2025年校園安全管理報告:校園安全與智慧校園安全教育模式
- 機電工程考試趨勢分析試題及答案
- 穿透式網(wǎng)絡(luò)攻擊防范試題及答案
- 機電工程的市場需求調(diào)研試題及答案
- 西方國家的憲法發(fā)展試題及答案
- 年產(chǎn)2億片阿奇霉素片劑的車間設(shè)計畢業(yè)作品
- 創(chuàng)業(yè)基礎(chǔ)(浙江財經(jīng)大學(xué))知到章節(jié)答案智慧樹2023年
- (44)-7.多毛細(xì)胞白血病
- 地質(zhì)雷達操作手冊2013版
- 英語牛津3000詞匯表
- GB/T 39204-2022信息安全技術(shù)關(guān)鍵信息基礎(chǔ)設(shè)施安全保護要求
- GB/T 36723-2018社會藝術(shù)水平考級專業(yè)分類要求
- 現(xiàn)代寫作教程
- 低壓電氣基礎(chǔ)知識培訓(xùn)課件
- 人民調(diào)解業(yè)務(wù)知識培訓(xùn)講座課件
- 武漢市第五醫(yī)院醫(yī)聯(lián)體探索和思考張斌課件
評論
0/150
提交評論