SpringBoot AbstractRoutingDataSource + MyBatis 多数据源配置/动态切换数据源

POM依赖如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

       <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

application.yml 配置如下

server:
  port: 8080

spring:
  application:
    name: multi-dataSources

  datasource:
    db1: # 数据库标识前缀 自定义
      jdbc-url: jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

    db2: # 数据库标识前缀 自定义
      jdbc-url: jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

简单建表测试,库一库二结构相同,数据不一样:

                            

启动类如下,我们要注入自己的数据源,所以需要事先排除SpringBoot数据库的自动配置行为:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan(basePackages = "com.xxx.mapper")
public class MultiDataSourcesApplication {
    public static void main(String[] args) {
        SpringApplication.run(MultiDataSourcesApplication.class, args);
    }
}
需要一个组件类,继承 AbstractRoutingDataSource 并实现 determineCurrentLookupKey 方法,这个方法会在确定目标数据源前调用,获得一个标识,从而获取我们配置的数据源,这里用ThreadLocal来保存。
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder= new ThreadLocal<String>();
    public static void setDataSourceKey(String dataSourceKey) {
        contextHolder.set(dataSourceKey);
    }
    public static String getDataSourceKey() {
        return contextHolder.get();
    }
    public static void clearDataSourceKey() {
        contextHolder.remove();
    }
}

在我们的配置类中注入数据源如下:

@SpringBootConfiguration
public class DataSourcesConfig {

    @Primary
    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource ds1() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource ds2() {
        return DataSourceBuilder.create().build();
    }

    // 注入我们的动态数据源
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("db1") DataSource db1, @Qualifier("db2") DataSource db2) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(db1);
        
        // 设置切换所需的所有动态数据源集合
        Map<Object, Object> oMap = new HashMap<>();
        oMap.put("db1", db1);
        oMap.put("db2", db2);
        dynamicDataSource.setTargetDataSources(oMap);
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        // 使用我们的动态数据源来构建SqlSessionFactory
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

再接下来就是来动态切换数据源了,这里我们来定义一个切面:

@Aspect
@Component
public class DynamicDattaSourceAspect {
    // 在目标方法调用前改变数据源指向
    @Before("@annotation(ds)")
    public void changeDataSource(ChangeDataSource ds) {
        DynamicDataSourceContextHolder.setDataSourceKey(ds.value());
    }

    // 在目标方法调用后清除线程变量
    @After("@annotation(ds)")
    public void clearDataSource(ChangeDataSource ds) {
        DynamicDataSourceContextHolder.clearDataSourceKey();
    }
}

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ChangeDataSource {
    String value();
}
最后,就是使用了:
@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    private UserService userService;
    @GetMapping("/db1")
    public Object db1() {
        return userService.db1();
    }
    @GetMapping("/db2")
    public Object db2() {
        return userService.db2();
    }
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    // 加上我们的自定义注解来切换数据源#查询db1的数据
    @ChangeDataSource("db1")
    public User db1() {
        return userMapper.userGet("1");
    }
    @Override
    // 加上我们的自定义注解来切换数据源#查询db2的数据
    @ChangeDataSource("db2")
    public User db2() {
        return userMapper.userGet("1");
    }
}

public interface UserMapper {
    @Select("select * from user where id = #{id}")
    User userGet(@Param("id") String id);
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String id;
    private String name;
}

最终测试结果:

                        ​​​​​​​       

测试成功,能够读取不同数据库的数据,此处不涉及事务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值