解锁无限可能:使用SpringBoot和MyBatis打造动态数据源

2024年 4月 30日 35.3k 0

今天分享一个利用SpringBoot和Mybatis实现动态加载数据源的过程,可以达到服务不停机迁移数据源的目的,废话不多说直接上代码。

首先 你需要在application.properties或application.yml文件中配置一个数据源的连接信息-用于存储配置信息,类似于以下示例:

# 主数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/test_test 
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

接下来,创建一个DataSourceDynamicConfig类来配置多个数据源

@Configuration
@ConditionalOnClass(value = {DataSource.class})
public class DataSourceDynamicConfig {

    String DEFAULT = "default";



    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean("defaultDataSource")
    public DataSource setDefault(){
        return DataSourceBuilder.create()
                .type(DruidDataSource.class).build();
    }

    @Bean
    public DynamicDataSource dynamicDataSource(@Qualifier("defaultDataSource") DataSource defaultDataSource) {
        Map targetDataSources = new HashMap();
        targetDataSources.put(DEFAULT, defaultDataSource);


        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(defaultDataSource);
        DataSourceContextHolder.setDataSourceName(DEFAULT);
        return dataSource;
    }


    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
        return factoryBean.getObject();
    }

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

}

在上述代码中,我们使用@ConfigurationProperties注解将不同数据源的配置注入到DataSource实例中。然后,我们创建一个DynamicDataSource实例。我们使用targetDataSources属性来存储所有数据源,每个数据源都有一个标识符。defaultTargetDataSource属性指定默认的数据源。

接下来,我们需要创建一个DynamicDataSource类来实现动态切换数据源。以下是一个简化的示例:

public class DynamicDataSource extends AbstractRoutingDataSource {

	// 存储所有数据源
    private static Map dataSources = new HashMap();


    public void setDataSources(Map sources){
        this.setTargetDataSources(sources);
        dataSources = sources;
    }

	
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceName();
    }


	// 动态添加数据源
    public static void setDataSource(String key,DataSource dataSource){
        dataSources.putIfAbsent(key,dataSource);
    }

    protected DataSource determineTargetDataSource() {
        String dataSourceName = determineCurrentLookupKey().toString();
        DataSource dataSource = (DataSource) dataSources.get(dataSourceName);
        if (dataSource == null) {
            throw new IllegalStateException("DataSource '" + dataSourceName + "' is not configured");
        }
        return dataSource;
    }

}
//存储当前线程数据源
public class DataSourceContextHolder {

    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal();

    public static void setDataSourceName(String name){
        CONTEXT_HOLDER.set(name);
    }

    public static String getDataSourceName(){
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSourceName(){
        CONTEXT_HOLDER.remove();
    }

}

添加一个数据实体,用于接收动态数据源。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataSourceEntity {

    String url;

    String userName;

    String pwd;

    String driverClassName;
}

接下来构造点模拟数据。

准备数据测试数据表语句。

create database test_test;
CREATE TABLE `t_t` (
  `id` int DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='test'

插入两个数据库测试数据。

## test_test
INSERT INTO test_test.t_t
(id, name)
VALUES(1, 'test_test');
## full_dev
INSERT INTO test_test.t_t
(id, name)
VALUES(1, 'full_dev');

创建一个查询TestMapper类。

@Mapper
public interface TestMapper {


    @Select("select * from t_t")
    Map selectMap();
}

创建SpringBoot Main函数。

@SpringBootApplication
@MapperScan(basePackages = {"com.xlc.common.datasource.mapper.test"})
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class,args);
    }
}

最后 单元测试。

@SpringBootTest(classes = Main.class)
@RunWith(SpringRunner.class)
public class TestApp {

    String userName = "root";

    String pwd = "root";

    String url = "jdbc:mysql://192.168.15.130:32691/test_test?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false";

    String driver = "com.mysql.cj.jdbc.Driver";


    @Resource
    TestMapper testMapper;


    @Before
    public void before(){
      DataSource dataSource =  DataSourceBuilder.create()
                .url(url)
                .username(userName)
                .password(pwd)
                .driverClassName(driver)
                .build();
        DynamicDataSource.setDataSource("hhh",dataSource);

    }

    @Test
    public void t2(){
        System.out.println(JSONUtil.toJsonStr(testMapper.selectMap()));
        DataSourceContextHolder.setDataSourceName("hhh");
        System.out.println(JSONUtil.toJsonStr(testMapper.selectMap()));

    }
}

运行结果:

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论