Mybatis动态数据源切换

Mybatis动态数据源切换

1. 结构设计

首先看一下这个功能的架构设计
在这里插入图片描述

  • 我们默认有一个缺省的数据源 Deault DataSource ,他是从配置文件中获取的,在应用刚开始启动时就注入,而在某些情况下,我们需要在一次操作中短时或长时间的对其它的数据库进行操作,这就是所谓的数据源切换。

  • 为了保证新添加的数据源不会对其它线程的操作有影响,我们使用ThreadLocal来存储当前使用的数据源的相关信息,创建上下文 DataSourceContextHolder 类,来保留当前线程的数据源。

  • 在Mybatis读取配置创建 Session 时,会注入 DynamicDataSource ,而 DynamicDataSource 通过读取当前线程变量可以获取自己设置的数据源,如果没有设置会注入默认的数据源,这个数据源来自于配置文件,调用流程如下图

在这里插入图片描述

2. 实现

public class DataSourceContextHolder {
    // 当前线程使用的数据源,为null表示默认数据源
    private static final ThreadLocal<DataSource> contextHolder = new InheritableThreadLocal<DataSource>();
    // 当前线程使用过的数据源,方便事务
    private static final List<DataSource> dataSources = new ArrayList<>();
    // 全局外部数据源缓存
    private static final HashMap<String, DataSource> map = new HashMap<>();

    // 设置当前线程的数据源
    public static void setDataSource(DruidDataSource datasource) {
        if (!map.containsKey(datasource.getUrl())) {
            contextHolder.set(datasource);
            map.put(datasource.getUrl(), datasource);
        }
        else {
            contextHolder.set(map.get(datasource.getUrl()));
        }
        dataSources.add(contextHolder.get());
    }

    // 获取数据源
    public static DataSource getDataSource() {
        return contextHolder.get();
    }

    // 获取数据源
    public static List<DataSource> getThreadDataSources() {
        return dataSources;
    }

    public static void clearCache() {
        map.clear();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }

}
@Configuration
public class DataSourceConfig {
    
    
    @Bean(name = "defaultDataSource")
    @ConfigurationProperties(prefix = "jdbc")
    public DataSource defaultDataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dynamicDataSource() {
        return new DynamicDataSource();
    }
}
/**
 * 动态数据源管理类
 */
public class DynamicDataSource extends AbstractDataSource {
	
    // 注入默认数据源
    @Resource(name = "defaultDataSource")
    private DataSource defaultDs;

    protected DataSource determineTargetDataSource() {
        // 获取当前线程的数据源
        DataSource dataSource = DataSourceContextHolder.getDataSource();
        // 如果没有设置动态数据源,则返回默认数据源
        if (dataSource == null) {
            return defaultDs;
        }
        return dataSource;
    }

    @Override
    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }
}
@Configuration
public class MyBatisConfig {

    @Resource(name = "dynamicDataSource")
    private DynamicDataSource dynamicDataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setObjectWrapperFactory(new MapWrapperFactory());
        // 设置数据源为DynamicDataSource
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("me.ezerror.pojo");
        return sqlSessionFactoryBean.getObject();
    }

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

}

3.使用方式

// 新建数据源
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setPassword(pwd);
dataSource.setUsername(username);
// 设置数据源
DataSourceContextHolder.setDataSource(dataSource);
// 数据库操作
List<Moment> moments = recordService.findMoment();
// 清除数据源,还原到默认数据源
DataSourceContextHolder.clearDataSource();
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
MyBatis切换数据源可以通过动态配置 SqlSessionFactory 的方式来实现。 首先,需要在项目中引入多个数据源的配置,例如在 Spring Boot 中可以将多个数据源配置在 application.yml 或 application.properties 文件中。 然后,可以通过编写一个继承自 AbstractRoutingDataSource 的动态数据源来进行数据源切换。该类需要重写 determineCurrentLookupKey 方法,该方法返回的值就是当前使用的数据源的 key。 最后,在配置 SqlSessionFactory 时,将上述动态数据源作为其数据源即可。 下面是一个示例代码: ```java @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } @Bean public DataSource dynamicDataSource() { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("primary", primaryDataSource()); targetDataSources.put("secondary", secondaryDataSource()); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(primaryDataSource()); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dynamicDataSource()); return sessionFactory.getObject(); } } ``` 其中,DynamicDataSource 类继承自 AbstractRoutingDataSource,其代码如下: ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceKey(); } } ``` DataSourceContextHolder 类用于存储当前使用的数据源的 key,可以使用 ThreadLocal 来实现。例如: ```java public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(() -> "primary"); public static void setDataSourceKey(String dataSourceKey) { CONTEXT_HOLDER.set(dataSourceKey); } public static String getDataSourceKey() { return CONTEXT_HOLDER.get(); } public static void clearDataSourceKey() { CONTEXT_HOLDER.remove(); } } ``` 在需要切换数据源的地方,可以调用 DataSourceContextHolder.setDataSourceKey 方法设置当前使用的数据源的 key。例如: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<User> listUsers() { DataSourceContextHolder.setDataSourceKey("secondary"); List<User> users = userMapper.listUsers(); DataSourceContextHolder.clearDataSourceKey(); return users; } } ``` 上述代码中,在调用 userMapper.listUsers 方法之前先设置当前使用的数据源为 secondary,调用之后再清除设置。这样就可以实现在运行时动态切换数据源的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值