Saas系统多租户方案---springboot+dynamic-datasource动态数据源的保姆级教程

目录

一、方案的基本原理说明

二、方案应用

1.准备工作

2. 引入依赖

3.配置文件配置数据源 

4.springboot项目注入配置类

5.重写spirngboot的run方法加载主库配置的所有数据源

6.动态数据源的使用   @DS("#header.companyCode") 


一、方案的基本原理说明

 1.方案说明

  1. 同一个应用+每个租户使用一个独立的库(共享DB, 使用独立Schema)
  2. 主库存储租户数据库和租户信息等,分库作为各租户库存储各租户资料数据,应用直接链接主库通过主库读取各租户的数据库链接信息采用JDBC的方式加载所有的数据源。通过租户的唯一标识(如企业编码)读取租户相应的库。具体实现通过Dynamic-Datasource实现

 2.Dynamic-Datasource实现原理

  1. 数据源自动配置:Dynamic-Datasource支持基于配置文件或注解的方式自动配置数据源,简化了数据源的初始化过程。
  2. 动态数据源切换:Dynamic-Datasource提供了动态切换数据源的功能,可以在运行时根据业务需求选择不同的数据源。
  3.  数据源路由:Dynamic-Datasource支持自定义数据源路由规则,可以根据请求参数、注解等信息路由到不同的数据源。
  4. 数据源连接池管理:Dynamic-Datasource内置了连接池管理功能,可以有效地管理和维护数据源连接,提高了系统的稳定性和性能。
  5. 灵活的数据源配置:Dynamic-Datasource支持自定义数据源配置,如连接池大小、连接超时时间等,满足了不同业务场景的需求。

二、方案应用

1.准备工作

创建主数据库及分数据库,主数据库创建企业信息及数据库链接信息表

2. 引入依赖
  1. 在pom.xml文件中加入以下依赖:

    <dependency>
    	<groupId>com.baomidou</groupId>
    	<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    	<version>3.5.0</version>
    </dependency>
3.配置文件配置数据源 
spring:
  application:
    name: saas
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.110.3:3306/saas
    username: xxx
    password: xxx
    type: com.zaxxer.hikari.HikariDataSource
    dynamic:
      enabled: true
4.springboot项目注入配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyDynamicDataSourceConfig {

    // 定义动态数据源处理器链的Bean
    @Bean
    public DsProcessor dsProcessor() {
        // 创建处理器链的起始点:头部信息处理器
        DsProcessor headerProcessor = new DsHeaderProcessor();
        // 创建会话处理器
        DsProcessor sessionProcessor = new DsSessionProcessor();
        // 创建SpEL表达式处理器
        DsProcessor spelProcessor = new DsSpelExpressionProcessor();
        // 设置处理器链的顺序:头部信息处理器 -> 会话处理器 -> SpEL表达式处理器
        headerProcessor.setNextProcessor(sessionProcessor);
        sessionProcessor.setNextProcessor(spelProcessor);
        // 返回处理器链的起始点
        return headerProcessor;
    }
}
//说明:
matches(String key) 方法检查键是否以 #header 开头,以确定是否由该处理器处理。
doDetermineDatasource(MethodInvocation invocation, String key) 方法根据请求中指定的头部信息键获取对应的头部值作为数据源。

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class DsHeaderProcessor extends DsProcessor {

    private static final String HEADER_PREFIX = "#header";

    // 判断是否匹配指定的键
    @Override
    public boolean matches(String key) {
        return key.startsWith(HEADER_PREFIX);
    }
    // 根据请求和键确定数据源
    @Override
    public String doDetermineDatasource(MethodInvocation invocation, String key) {
        // 获取当前请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 提取真实的头部信息键(去除前缀 "#header")
        String headerName = key.substring(8);
        // 根据头部信息键获取对应的头部值
        return request.getHeader(headerName);
    }
}
5.重写spirngboot的run方法加载主库配置的所有数据源
@Component
@Slf4j
public class CompanyDataSourceInit implements ApplicationRunner {
    @Autowired
    CompanyService companyService;
    private DataSource dataSource;
    private DefaultDataSourceCreator dataSourceCreator;

    @Autowired
    public CompanyDataSourceInit(DataSource dataSource, DefaultDataSourceCreator dataSourceCreator) {
        this.dataSource = dataSource;
        this.dataSourceCreator = dataSourceCreator;
    }
    /**
     * 重写run 加载企业配置的数据库
     *
     * @param args
     */
    @Override
    public void run(ApplicationArguments args) {
        List<CompanyEntity> entities = companyService.findCompanyEntitiesByState();
        log.info("动态数据库配置开始!最终数据源总数量:{}", entities.size());
        // 当前动态数据源
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        entities.forEach((item) -> {
            // 数据源参数配置
            DataSourceProperty dataSourceProperty = new DataSourceProperty();
            String dbClassType = "com.mysql.jdbc.Driver";
            String dbUrl = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=utf8&useSSL=false", item.getDbIp(), item.getDbPort(), item.getDbName());
            dataSourceProperty.setPoolName(item.getCompanyCode());
            dataSourceProperty.setUsername(item.getDbUser());
            dataSourceProperty.setPassword(item.getDbPassword());
            dataSourceProperty.setUrl(dbUrl);
            dataSourceProperty.setDriverClassName(dbClassType);
            // 数据源全部懒加载,避免一次性声明过多连接
            dataSourceProperty.setLazy(true);
            // 生成数据源
            DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
                // 添加到动态数据源中
                ds.addDataSource(item.getCompanyCode(), dataSource);
                log.info("当前加载的数据源:{},加载完成!", item.getCompanyCode());
            });
            log.info("动态数据库配置结束!总共加载{}个数据源!", ds.getDataSources().size());
        }
    }
}
6.动态数据源的使用   @DS("#header.companyCode") 
//说明:租户登录时将确认数据库的唯一标识存入请求头中
使用@DS("#header.companyCode")来指定数据库  可放controller,service,mapper层及方法上

@DS("#header.companyCode")   //通过读取请求头里面租户的唯一标识来实现指定数据库
@Slf4j
public class SaasServiceImpl implements SaasService {

}

三、总结

Dynamic-Datasource 实现多JDBC数据源配置的好处包括灵活的数据源选择、负载均衡和性能优化、容错和高可用性、分布式事务支持以及简化的管理和维护。这些特性使得它成为处理复杂应用场景下数据库访问需求的理想选择。

好,以上就是全部内容,能坚持看到这里,你一定很有收获,那么动一动拿offer的小手,点个赞再走吧,听说这么做的人2024年都交了好运!!!!

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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的多租户动态数据源模式了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值