SAAS下一个mysql实例多个租户的库

在多租户环境中,根据tenantCode字段动态切换数据库是常见的需求。这里将展示如何在Spring Boot和MyBatis项目中实现这一功能,具体步骤包括配置数据源、定义数据源路由逻辑以及在业务代码中使用。

1. 配置数据源

首先,你需要为你的应用配置一个主数据源,这个数据源将被用于连接到包含所有租户数据库的MySQL实例。然后,创建一个动态数据源,该数据源将根据tenantCode来决定使用哪个具体的数据库。

主数据源配置

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    public DataSource primaryDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306"); // 注意这里只提供实例URL,不指定数据库
        dataSource.setUsername("your_username");
        dataSource.setPassword("your_password");
        return dataSource;
    }

    @Bean
    public DynamicTenantDataSource dynamicTenantDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        return new DynamicTenantDataSource(primaryDataSource);
    }
}

2. 定义动态数据源路由逻辑

接下来,你需要创建一个DynamicTenantDataSource类,它将继承AbstractRoutingDataSource并实现数据源的动态切换逻辑。此外,还需要一个TenantContext类来存储当前线程的tenantCode

public class DynamicTenantDataSource extends AbstractRoutingDataSource {

    private final Map<String, DataSource> tenantDataSources = new HashMap<>();

    public DynamicTenantDataSource(DataSource defaultDataSource) {
        super.setDefaultTargetDataSource(defaultDataSource);
    }

    public void addTenantDataSource(String tenantId, String databaseName) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/" + databaseName);
        dataSource.setUsername("your_username");
        dataSource.setPassword("your_password");
        tenantDataSources.put(tenantId, dataSource);
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getTenantCode();
    }

    public void removeTenantDataSource(String tenantId) {
        tenantDataSources.remove(tenantId);
    }
}

public class TenantContext {

    private static final ThreadLocal<String> tenantCodeHolder = new ThreadLocal<>();

    public static void setTenantCode(String tenantCode) {
        tenantCodeHolder.set(tenantCode);
    }

    public static String getTenantCode() {
        return tenantCodeHolder.get();
    }

    public static void clearTenantCode() {
        tenantCodeHolder.remove();
    }
}

3. 在业务代码中使用

现在,你可以在业务代码中根据tenantCode来切换数据源了。通常情况下,这会在服务层或过滤器中完成,以确保在处理请求之前设置正确的tenantCode,并在请求结束后清除它。

设置tenantCode

@Service
public class TenantService {

    @PostConstruct
    public void init() {
        ((DynamicTenantDataSource) dataSource()).addTenantDataSource("tenant1", "tenant_db_1");
        ((DynamicTenantDataSource) dataSource()).addTenantDataSource("tenant2", "tenant_db_2");
    }

    @Autowired
    private DataSource dataSource;

    public void processRequest(String tenantCode) {
        TenantContext.setTenantCode(tenantCode);
        try {
            // 执行数据库操作
        } finally {
            TenantContext.clearTenantCode();
        }
    }
}

注意事项

  • 确保在请求结束时清除tenantCode,避免影响后续请求。
  • 如果有多个线程同时访问,需要保证DynamicTenantDataSource的线程安全。
  • 在实际部署时,tenantCode和数据库名称的映射可能需要从配置文件或外部服务读取,而不是硬编码在代码中。

 

posted @ 2024-07-04 17:16  使用D  阅读(4)  评论(0编辑  收藏  举报