解锁无限可能:使用SpringBoot和MyBatis打造动态数据源
今天分享一个利用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())); } }
运行结果: