springboot+mybatis+druid实现数据源动态切换

扣扣分享交流群:1125844267
提示:
本文主要流程参考博客:https://www.cnblogs.com/xifengxiaoma/p/9888240.html
集成druid数据库连接池参考博客:https://www.cnblogs.com/dyc940210/p/9334589.html


配置步骤

1.配置文件中配置多数据源信息

代码如下:

datasource:
    # 使用druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    master:
      url: jdbc:mysql://ip:3306/数据库?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
      username: 用户名
      password: 密码
      driver-class-name: com.mysql.cj.jdbc.Driver
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
    slave:
      url: jdbc:mysql://ip:3306/数据库?useSSL=false&useUnicode=yes&characterEncoding=UTF-8
        username: 用户名
        password: 密码
        driver-class-name: com.mysql.cj.jdbc.Driver
        filters: stat
        maxActive: 20
        initialSize: 1
        maxWait: 60000
        minIdle: 1
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: select 'x'
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxOpenPreparedStatements: 20

2.启动类配置

主要是添加注解,禁用数据源默认自动配置
代码如下:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

3.mybatis配置

代码如下:

/**
 * mybatis配置
 */
@Configuration
@MapperScan(basePackages = {"cn.xxxx.xx.mapper"})
public class MybatisConfig {
    //数据源类型:连接池
    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;

    @Bean("master")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource master() {
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean("slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slave() {
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put("master", master());
        dataSourceMap.put("slave", slave());
        // 将 master 数据源作为默认指定的数据源
        dynamicDataSource.setDefaultDataSource(master());
        // 将 master 和 slave 数据源作为指定的数据源
        dynamicDataSource.setDataSources(dataSourceMap);
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
        sessionFactory.setDataSource(dynamicDataSource());
        sessionFactory.setTypeAliasesPackage("cn.xxxx.xx.bean.*");
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactory.setMapperLocations(resolver.getResources("classpath*:**/mapping/*.xml"));    // 扫描映射文件
        return sessionFactory;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        // 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
        return new DataSourceTransactionManager(dynamicDataSource());
    }
}

4.动态数据源实现类

代码如下:

/**
 * 动态数据源实现类
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源
     * 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
     */
    @Override
    protected DataSource determineTargetDataSource() {
        return super.determineTargetDataSource();
    }

    /**
     * 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }

    /**
     * 设置默认数据源
     * @param defaultDataSource
     */
    public void setDefaultDataSource(Object defaultDataSource) {
        super.setDefaultTargetDataSource(defaultDataSource);
    }

    /**
     * 设置数据源
     * @param dataSources
     */
    public void setDataSources(Map<Object, Object> dataSources) {
        super.setTargetDataSources(dataSources);
        // 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
        DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
    }
}

5.动态数据源上下文

代码如下:

/**
 * 动态数据源上下文
 */
public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
        /**
         * 将 master 数据源的 key作为默认数据源的 key
         */
        @Override
        protected String initialValue() {
            return "master";
        }
    };


    /**
     * 数据源的 key集合,用于切换时判断数据源是否存在
     */
    public static List<Object> dataSourceKeys = new ArrayList<>();

    /**
     * 切换数据源
     * @param key
     */
    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    /**
     * 获取数据源
     * @return
     */
    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    /**
     * 重置数据源
     */
    public static void clearDataSourceKey() {
        contextHolder.remove();
    }

    /**
     * 判断是否包含数据源
     * @param key 数据源key
     * @return
     */
    public static boolean containDataSourceKey(String key) {
        return dataSourceKeys.contains(key);
    }

    /**
     * 添加数据源keys
     * @param keys
     * @return
     */
    public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
        return dataSourceKeys.addAll(keys);
    }
}

那么到了这一步其实就可以实现数据源的切换,只要调用 DynamicDataSourceContextHolder.setDataSourceKey(key) 就可以完成了,使用完成后再设置回默认数据源DynamicDataSourceContextHolder.clearDataSourceKey()。


6.注解AOP配置

使用注解和AOP的方式简化我们的操作,在添加注解的方法前后分别切换数据库。
代码如下:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    /**
     * 数据源key值
     * @return
     */
    String value();
}


@Aspect
@Order(-1)  // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {
    /**
     * 切换数据源
     * @param point
     * @param dataSource
     */
    @Before("@annotation(dataSource))")
    public void switchDataSource(JoinPoint point, DataSource dataSource) {
        if (!DynamicDataSourceContextHolder.containDataSourceKey(dataSource.value())) {
            System.out.println("DataSource [{}] doesn't exist, use default DataSource [{}] " + dataSource.value());
        } else {
            // 切换数据源
            DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value());
            System.out.println("Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
                    + "] in Method [" + point.getSignature() + "]");
        }
    }

    /**
     * 重置数据源
     * @param point
     * @param dataSource
     */
    @After("@annotation(dataSource))")
    public void restoreDataSource(JoinPoint point, DataSource dataSource) {
        // 将数据源置为默认数据源
        DynamicDataSourceContextHolder.clearDataSourceKey();
        System.out.println("Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
                + "] in Method [" + point.getSignature() + "]");
    }
}

7.注解使用

比如在一个service实现类的方法上加注解@DataSource(“slave”),将数据源切换为slave数据源。那么这样做可能有一个弊端就是不够灵活,注解的这个方法中的所有数据源只能都是slave。
代码如下:

	@DataSource("slave")
    @Override
    public List<Map> getAllTask() {
        return taskMapper.getAllTask();
    }

总结

本文主要是记录一下代码流程,如果想要更详细的流程解析,大家可以参考博客一。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 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
发出的红包

打赏作者

Happy-Sir

有收获请打赏,哈哈

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值