springboot 多数据源配置和路由

简介

基于spring的AbstractRoutingDataSource接口实现的多数据源路由功能,实现不同数据源之间切换

Git地址

https://gitee.com/wqrzsy/lp-demo/tree/master/lp-datasource-router

更多demo请关注

springboot demo实战项目
java 脑洞
java 面试宝典
开源工具

项目分析

  1. 多数据源配置
spring:
  datasource:
    d1:
      platform: mysql
      jdbc-url: jdbc:mysql://localhost:13306/test1?serverTimezone=GMT&useSSL=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    d2:
      platform: mysql
      #      HikariDataSource
      jdbc-url: jdbc:mysql://localhost:13306/test2?serverTimezone=GMT&useSSL=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

配置类DataSourceConfiguration
通过@ConfigurationProperties注解实现不同属性注入DataSource,通过 @Primary注解标识DynamicRouterDataSource类,那其他地方拿DataSource时拿到就是代理过的DynamicRouterDataSource类

    @Bean(value = FIRST_DATASOURCE_KEY)
    @Qualifier(FIRST_DATASOURCE_KEY)
    // ConfigurationProperties 指定属性前缀,忽略那些不能绑定到 @ConfigurationProperties 类字段的属性
    @ConfigurationProperties(prefix = "spring.datasource.d1")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean(value = SECOND_DATASOURCE_KEY)
    @Qualifier(SECOND_DATASOURCE_KEY)
    // ConfigurationProperties 指定属性前缀,忽略那些不能绑定到 @ConfigurationProperties 类字段的属性
    @ConfigurationProperties(prefix = "spring.datasource.d2")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Primary
    @Bean(name = "routingDataSource")
    public AbstractRoutingDataSource routingDataSource(
        @Qualifier(FIRST_DATASOURCE_KEY) DataSource d1,
        @Qualifier(SECOND_DATASOURCE_KEY) DataSource d2
    ) {
        DynamicRouterDataSource proxy = new DynamicRouterDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put("masterDataSource", d1);
        targetDataSources.put("slaveDataSource", d2);
        proxy.setDefaultTargetDataSource(d1);
        proxy.setTargetDataSources(targetDataSources);
        DataSourceRouterContext.setDefaultDataSource("masterDataSource");
        return proxy;
    }
  1. 准备路由注解
/**
 * 数据库路由注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Transactional(propagation = Propagation.REQUIRES_NEW)
public @interface DataSourceRouter {

    String value();

}
  1. 路由AOP,这里@Order(-1)是关键,要优先@Transactional切换,在事务之前就切换好数据库
/**
 * 数据库路由AOP
 */
@Aspect
@Component
@Order(-1)
public class DataSourceRouterAOP {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceRouterAOP.class);

    /**
     * 此处的切点是注解的方式,也可以用包名的方式达到相同的效果
     * '@Pointcut("execution(* com.wwj.springboot.service.impl.*.*(..))")'
     */
    @Pointcut("@annotation(DataSourceRouter)")
    public void delayInvoke() {

    }


    /**
     * 环绕增强,相当于MethodInterceptor
     */
    @Around("delayInvoke()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取DataSourceRouter 注解获取数据源名字
        DataSourceRouter dataSourceRouter = method.getAnnotation(DataSourceRouter.class);
        // 通过DataSourceRouterContext指定当前数据源
        DataSourceRouterContext.setDataSource(dataSourceRouter.value());
        try {
            // 调用方法
            return joinPoint.proceed();
        } finally {
            // 清空环境
            DataSourceRouterContext.clear();
        }
    }

}

4.调用实例

/**
 * 测试路由服务
 */
@Service
public class TestRouterService {

    @Autowired
    private TestEntityDAO testEntityDAO;

    @DataSourceRouter(value = "masterDataSource")
    public void testD1() {
        TestEntity entity = TestEntity.of(System.currentTimeMillis(), "123", 1);
        testEntityDAO.save(entity);
    }

    @DataSourceRouter(value = "slaveDataSource")
    public void testD2() {
        TestEntity entity = TestEntity.of(System.currentTimeMillis(), "123", 1);
        testEntityDAO.save(entity);
    }

}

注意

  1. 使用@DataSourceRouter时,会每次都会新建一个事务,这里使用时需注意
  2. 需要建立数据库test1和test2,然后导入resources下的test.sql文件建表
    image.png

demo项目导入

参考: https://www.jianshu.com/p/cd0275a2f5fb

#公众号
五分钟了解前沿技术,大数据,微服务,区域链,提供java前沿技术干货,独立游戏制作技术分享

五分钟技术

如果这篇文章对你有帮助请给个star
image.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值