Springboot +Mybatis + DynamicDataSource 实现动态切换数据源

DynamicDataSource 动态数据源

需求:

数仓项目需要大致检验数据同步的正确性,有多台MySQL业务数据库,一台数据仓库Postgres。需要检验MySQL各个库中表的条数与postgres中表的条数是否一致。

问题:

Mybatis+MySQL操作每个数据库,需要配置每个数据库的数据源,如配置 url:jdbc://mysql:localhost:8081/user 。
Postgres则没有此烦恼,所有表均在一个库中。 只需要配置一次数据源。
但是MySQL业务数据存储到不同服务器上,且每台数据库服务器上有数十个库。如果使用常规的Springboot + mybatis,通过application.yml来配置数据源,显然不切实际。因此,也考虑过使用原生的jdbc来操纵数据库,但是频繁创建数据源和连接数据库存在性能问题。

解决方案:

使用动态DynamicDataSource ,将数据源信息存储至Postgres中一个映射表,通过读取映射来配置数据源。最后,通过key的值来切换数据源,实现数据增删改查。

实现过程

pom.xml
 <!-- postgres-->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>${postgresql.version}</version>
    </dependency>
    <!-- mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
        <version>8.0.13</version>
    </dependency>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${starter.mybatis.spring.version}</version>
    </dependency>
    <!-- DynamicDataSource -->
    <dependency>
        <groupId>com.github.yeecode.dynamicdatasource</groupId>
        <artifactId>DynamicDataSource</artifactId>
        <version>1.3.0</version>
    </dependency>
配置applicaiton.yml设置默认数据源
dynamicDataSource:
  default:
    url: jdbc:postgresql://localhost:5432/postgres
    driverClassName: org.postgresql.Driver
    username: 
    password: 
配置spirngboot启动器

1.去除自动配置数据源

 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) 

2.扫描包

@ComponentScan(basePackages = {"com.github.yeecode.dynamicdatasource","业务包"})
新增数据源
public void addDynamicDataSource(List<CheckResult> checkResultList) {
    String pgDriverClass = "org.postgresql.Driver";
    String pgUrl = "jdbc:postgresql://localhost:5432/postgres";
    String pgUserName = "";
    String pgPassword = "";
    List<DataSourceInfo> allDataSource = getAllDataSource(checkResultList);
    if (CollectionUtils.isEmpty(allDataSource)) {
        return;
    }

    //-----------------------    Mysql 数据源    ---------------------
    for (DataSourceInfo dataSourceInfo : allDataSource) {
        String name = dataSourceInfo.getHostName() + " " + dataSourceInfo.getDatabaseName(); //根据host databasename组合来查找对应的数据源
        com.github.yeecode.dynamicdatasource.model.DataSourceInfo mysqlDynamicDataSourceInfo = new com.github.yeecode.dynamicdatasource.model.DataSourceInfo(name, dataSourceInfo.getDriverClassName(), dataSourceInfo.getUrl(), dataSourceInfo.getUsername(), dataSourceInfo.getPassword());
        dynamicDataSource.addDataSource(mysqlDynamicDataSourceInfo, true);
    }


    //-----------------------    Postgres数据源    ---------------------
    com.github.yeecode.dynamicdatasource.model.DataSourceInfo pgDynamicDataSourceInfo = new com.github.yeecode.dynamicdatasource.model.DataSourceInfo("postgres", pgDriverClass, pgUrl, pgUserName, pgPassword);
    dynamicDataSource.addDataSource(pgDynamicDataSourceInfo, true);


}
操作数据库
public List<CheckResult> check() {
    List<CheckResult> resultList = new LinkedList<>(); //存储检查结果

    List<CheckResult> dataSourceInfoList = checkResultMapper.listByGroup();  // 根据映射表获取数据源信息

    dataSourceInfoService.addDynamicDataSource(dataSourceInfoList); // 新增数据源

    List<CheckResult> mappingList = checkResultMapper.findAll();

    if (CollectionUtils.isEmpty(mappingList)) {
        return null;
    }

    //-----------------------    根据每个表来查询对应的条数    ---------------------
    for (CheckResult checkResult : mappingList) {
        String originDatabase = checkResult.getOriginDatabase();
        String originDatabaseHost = checkResult.getOriginDatabaseHost();
        String originTableName = checkResult.getOriginTableName();
        String targetTableName = checkResult.getTargetTableName();
        String timeFieldName = checkResult.getTimeFieldName();
        String timeFieldValue = checkResult.getTimeFieldValue();
        String originCheckSQL = getCheckSQL(originTableName, timeFieldName, timeFieldValue);
        String targetCheckSQL = getCheckSQL(targetTableName, timeFieldName, timeFieldValue);

        //-----------------------    计算源数据库条数    ---------------------
        String datasourceKey = originDatabaseHost + " " + originDatabase;  //根据该key来切换数据源
        dynamicDataSource.switchDataSource(datasourceKey);
        long originCount = commonService.getCount(originCheckSQL); // 源库条数

        //-----------------------    计算目标库条数    ---------------------
        dynamicDataSource.switchDataSource("postgres");
        long targetCount = commonService.getCount(targetCheckSQL); // 目标库条数

        //-----------------------    汇总结果    ---------------------
        checkResult.setOriginCount(originCount);
        checkResult.setTargetCount(targetCount);
        checkResult.setSubValue(targetCount - originCount);
        if (originCount == targetCount) {
            checkResult.setIsCorrect(true);
        } else {
            checkResult.setIsCorrect(false);
        }
        resultList.add(checkResult);

    }
    return resultList;
}

第一次写文章,如有更好建议和意见,请多多指教!

Springboot+MyBatis-Plus实现多租户动态数据源模式是一种在Spring Boot框架下使用MyBatis-Plus插件实现多租户数据隔离的方法。它可以根据不同的租户动态切换数据源实现不同租户之间的数据隔离。 实现多租户动态数据源模式的关键是配置多个数据源,并在运行时根据租户信息动态选择使用哪个数据源。以下是一个简单的示例代码: 1. 首先,需要在pom.xml文件中添加Druid数据源的依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> ``` 2. 在application.properties或application.yml文件中配置多个数据源连接信息,例如: ```yaml spring.datasource.master.url=jdbc:mysql://localhost:3306/master_db spring.datasource.master.username=root spring.datasource.master.password=123456 spring.datasource.tenant1.url=jdbc:mysql://localhost:3306/tenant1_db spring.datasource.tenant1.username=root spring.datasource.tenant1.password=123456 spring.datasource.tenant2.url=jdbc:mysql://localhost:3306/tenant2_db spring.datasource.tenant2.username=root spring.datasource.tenant2.password=123456 ``` 3. 创建一个多租户数据源配置类,用于动态选择数据源。可以使用ThreadLocal来保存当前租户的标识,然后根据标识选择对应的数据源。以下是一个简单的示例: ```java @Configuration public class MultiTenantDataSourceConfig { @Autowired private DataSourceProperties dataSourceProperties; @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.tenant1") public DataSource tenant1DataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.tenant2") public DataSource tenant2DataSource() { return DataSourceBuilder.create().build(); } @Bean @Primary public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(); dataSourceMap.put("master", masterDataSource()); dataSourceMap.put("tenant1", tenant1DataSource()); dataSourceMap.put("tenant2", tenant2DataSource()); dynamicDataSource.setTargetDataSources(dataSourceMap); dynamicDataSource.setDefaultTargetDataSource(masterDataSource()); return dynamicDataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dynamicDataSource); return sessionFactory.getObject(); } @Bean public PlatformTransactionManager transactionManager(DataSource dynamicDataSource) { return new DataSourceTransactionManager(dynamicDataSource); } } ``` 4. 创建一个多租户数据源切换器,用于在每次数据库操作前切换数据源。以下是一个简单的示例: ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return TenantContext.getTenantId(); } } ``` 5. 创建一个租户上下文类,用于保存当前租户的标识。以下是一个简单的示例: ```java public class TenantContext { private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>(); public static void setTenantId(String tenantId) { CONTEXT.set(tenantId); } public static String getTenantId() { return CONTEXT.get(); } public static void clear() { CONTEXT.remove(); } } ``` 6. 在需要切换数据源的地方,调用TenantContext.setTenantId()方法设置当前租户的标识。例如: ```java @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/users") public List<User> getUsers() { TenantContext.setTenantId("tenant1"); List<User> users = userService.getUsers(); TenantContext.clear(); return users; } } ``` 通过以上步骤,就可以实现Springboot+MyBatis-Plus的多租户动态数据源模式了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值