栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用Spring的AbstractRoutingDataSource实现动态数据源

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用Spring的AbstractRoutingDataSource实现动态数据源

前言

笔者公司目前数据库进行了分库操作,分成了几个库,存放用户相关信息的user库,存放资产信息的asset库,存放直播相关信息的live库。在平时开发过程中涉及多库操作的地方,需要通过rpc调用对应服务。这样的开发模型大体上没问题,因为既然拆分库了,应用跟随数据库进行拆分,也能防止以后开发代码混乱。但是这样缺少了灵活性,所以我这里为我们项目增添多数据源功能。

调研

调研了两种spring+mybatis数据源方案。

1 多份配置

在项目中,为每个库分别设置一套数据源配置。
DataSource,SqlSessionFactory,MapperScannerConfigurer分别对应数据源配置多份,
这里要注意的是mapper.xml文件地址也需要根据数据源来进行分包存放。由于我们项目xml文件过多,迁移麻烦,同时也感觉这种方式不灵活,所以并没有使用此种方式。

2 使用AbstractRoutingDataSource

这是spring提供的多数据源方式,配合注解和aop可以灵活进行数据源的切换,非常好用。下面重点进行这种方式的介绍。

2.1 配置枚举

对应不同数据源设置美剧

    public static enum DataSourceEnum {
        USER, ASSET
    }
2.2 DataSource ThreadLocal

将数据源枚举放进线程本地缓存,方便后续切换。

   public static class DataSourceTypeHolder {
       private static final ThreadLocal dataSourceTypes = ThreadLocal.withInitial(() -> DataSourceEnum.USER);
       public static DataSourceEnum get() {
           return dataSourceTypes.get();
       }
       public static void set(DataSourceEnum dataSourceType) {
           dataSourceTypes.set(dataSourceType);
       }
   }
2.3 配置多数据源

如下代码所示,分别对应两个库设置两个数据源。

@Bean
   public DataSource user() {
       MysqlDataSource source = new MysqlDataSource();
       source.setURL("jdbc:mysql://localhost:3306/user");
       source.setUser("root");
       source.setPassword("123456");
       return source;
   }

   @Bean
   public DataSource asset() {
       MysqlDataSource source = new MysqlDataSource();
       source.setURL("jdbc:mysql://localhost:3306/asset");
       source.setUser("root");
       source.setPassword("123456");
       return source;
   }
2.4 配置DynamicDataSource

自定义多数据源继承 AbstractRoutingDataSource, 将多数据源设置到动态数据源,方便后续进行切换,并交由spring管理。

   public static class DynamicDataSource extends AbstractRoutingDataSource {
       @Override
       protected Object determineCurrentLookupKey() {
           return DataSourceTypeManager.get();
       }
   }

   @Bean
   public DynamicDataSource dynamicDataSource() {
       DynamicDataSource dataSource = new DynamicDataSource();
       dataSource.setDefaultTargetDataSource(master());
       HashMap map = new HashMap<>();
       map.put(DataSourceEnum.USER, user());
       map.put(DataSourceEnum.ASSET, asset());
       dataSource.setTargetDataSources(map);
       dataSource.afterPropertiesSet();
       return dataSource;
   }
2.5 其他mybatis配置
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(this.dynamicDataSource());
        bean.setMapperLocations(new ClassPathResource("io/piper/server/spring/pojo/mapper/xml/UserMapper.xml"));
        return bean;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer config = new MapperScannerConfigurer();
        config.setbasePackage("io/piper/server/spring/pojo/mapper");
        return config;
    }

    @Bean
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(user());
        return manager;
    }
2.6 业务代码示例

如下,执行数据库操作之前,首先通过ThreadLocal设置将要使用的数据源。

 public String find() {
        Config.DataSourceTypeHolder.set(Config.DataSourceEnum.ASSET);
        return JSON.toJSONString(userMapper.selectByExample(null), true);
    }
2.7 注解切换数据源

以上业务已经能够满足我们业务数据源切换需求,不过如果能结合注解进行切换岂不是更好,那样就不用手动操作了。
如下定义注解,并配置aop拦截方法上边有自定义注解的方法,解析要使用的数据源,进行动态切换。

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Routing {
        DataSourceEnum value() default DataSourceEnum.USER;
    }

    @Component
    @org.aspectj.lang.annotation.Aspect
    public static class Aspect {
        @Around("@annotation(io.piper.datasource.Config.Routing)")
        public void before(ProceedingJoinPoint jp) {
            MethodSignature signature = (MethodSignature) jp.getSignature();
            Routing routing = signature.getMethod().getAnnotation(Routing.class);
            try {
                DataSourceTypeHolder.set(routing.value());
                jp.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }

最终,我们的业务代码如下,是不是就简单多了呢?

    @Config.Routing(Config.DataSourceEnum.ASSET)
    public String find() {
        return JSON.toJSONString(userMapper.selectByExample(null), true);
    }
结语

以上代码已经上传到 我的码云 点击进去找 routing-data-source

另外推荐下我的开源作品 PiperChat
PiperChat 是一款简洁高效的即时通讯服务,提供多种技术供开发者选择,帮助开发者快速构建高并发的即时通讯服务。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/584683.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号