Druid多数据源配置&Datasurce动态切换

来源

  • 业务数据存在数据源A,统计数据存在数据源B(表不同)
  • 应用拆分,灰度切换数据源,部分数据使用数据源A,部分数据使用数据源C(表相同)

实现

场景1:

不同的表结构存在不同的库:数据源A和数据源B不同的表
使用MapperScan,不同的DAO绑定不同的数据源

数据源A 配置
@Configuration
@ConfigurationProperties(prefix = "spring.datasourceA")
@EnableTransactionManagement
@MapperScan(basePackages = {"com.xxx.xxx.xxx","com.xxx.xxx.yyy"})
public class DruidConfig implements TransactionManagementConfigurer {
...
...
数据源B 配置
@Configuration
@ConfigurationProperties(prefix = "spring.datasourceB")
@EnableTransactionManagement
@MapperScan(basePackages = {"com.xxx.xxx.zzz"})
public class DruidConfig implements TransactionManagementConfigurer {
...
...

场景2:

同样的表结构存在不同的库:数据源A 和 数据源C,表相同,根据启动环境或者业务判断选用不同的数据源
使用AbstractRoutingDataSourcedetermineCurrentLookupKey决定当前线程使用哪个数据源

But一开始的想法(被抛弃和走的弯路):

  1. 使用场景1的方式,配置两个不同的数据源,拷贝俩份DAO和mapper.xml,代码判断使用哪个数据源的DAO
  2. 使用场景1的方式,配置两个不同的数据源,拷贝俩份DAO和mapper.xml,通过注解和切面,切换DAO
  • 自定义注解AliasSource
@AliasSource("xxxcccDAO") // 另一个DAO的bean name
@Repository("xxxDAO")
public interface XxxDAO {
...
...
  • 自定义切面
@Aspect
@Component
public class AliasSourceAspect {
    @Pointcut("@within(com.xxx.annotation.AliasSource)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
            // 通过判断环境或者线程变量 判断是否使用注解指定的bean,反射调用之
            Method method = ((MethodSignature) pjp.getSignature()).getMethod();
            Class<?> clazz = method.getDeclaringClass();
            AliasSource annotation = clazz.getAnnotation(AliasSource.class);
            String name = annotation.value();
            Object bean = SpringBeanUtil.getBean(name);
            Method realMethod = bean.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
            return realMethod.invoke(bean, pjp.getArgs());
            
            // 不然直接反射调用原来的方法
            return pjp.proceed();
        } 
    }

  1. 使用了AbstractRoutingDataSourcedetermineCurrentLookupKey决定当前线程使用哪个数据源,但是切面切的不对,切DAO的时候为时过晚
@Aspect
@Component
public class DataSourceAspect {
    @Pointcut("(execution(public * com.xxx.xxx.xxx.dal..*(..)) 
                      || execution(public * com.xxx.xxx.yyy.dal..*(..))) 
                      && !execution(public * com.xxx.xxx.zzz.dal..*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
            // 通过判断环境或者业务类型,来指定线程变量
            if (环境A/C) {
                // 下面代码有解释这个环境变量,指定数据源用的,
                // 这里其实已经晚了,走到这个切面的时候,
                // 已经调用过`AbstractRoutingDataSource`的`determineCurrentLookupKey`先选好数据源了,得到connection了
                线程变量.set("dbA/dbC")`;
            }
            // 调用dao方法
            return pjp.proceed();
        } 
    }

原因1:在service层使用了事务(@TransactionalORtransactionTemplate.execute(new TransactionCallbackWithoutResult() {})),这中场景肯定会先获取connection,开启事务,然后才走到我们自己的切面,晚了
原因2:由于切面本身也是切的DAO,但是spirng mybatis自己的切面在前,先获取了connection,然后才走到我们自己的切面,晚了
SO~ 去掉切面,改成在webapp的filter(自定义DataSourceFilter)就先设置好环境变量(线程变量.set("dbA/dbC"))选择好当前线程使用的数据源;这样就能正确的拿到指定数据源的connection啦

数据源A 和 数据源C 配置
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")// 注入两份配置
@EnableTransactionManagement
@MapperScan(basePackages = {"com.xxx.xxx.xxx","com.xxx.xxx.yyy"})
public class DruidConfig implements TransactionManagementConfigurer {
...
...
    @Bean
    @Primary
    @Override
    public DataSourceTransactionManager annotationDrivenTransactionManager() {
        DruidDataSource dataSourceA = createDruidDataSource(urlA, usernameA, passwordA);
        DruidDataSource dataSourceC = createDruidDataSource(urlC, usernameC, passwordC);
        AbstractRoutingDataSource dynamicDataSource = new AbstractRoutingDataSource() {
            @Override
            protected Object determineCurrentLookupKey() {
                // 1. 可以通过启动环境判断使用哪个数据源
                if (环境A/C) {
                    return "dbA/dbC";
                }
                // 2. 可以通过自己设置的线程变量的值来指定使用哪个数据源
                String db = 线程变量.get();
                return db;
            }
        };
        Map<Object, Object> dsMap = Maps.newHashMap();
        dsMap.put("dbA", dataSourceA);
        dsMap.put("dbC", dataSourceC);
        dynamicDataSource.setDefaultTargetDataSource(salaryDruidDataSource);
        dynamicDataSource.setTargetDataSources(dsMap);
        dynamicDataSource.afterPropertiesSet();
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    private DruidDataSource createDruidDataSource(String url, String username, String password) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClassName);
        ...
        ...
        return dataSource;
    }
...
...

参考

druid多数据源配置+Datasurce动态切换

AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343