大家好,我是半夏之沫 😁😁 一名金融科技領域的JAVA系統研發😊😊
我希望將自己工作和學習中的經驗以最樸實,最嚴謹的方式分享給大家,共同進步👉💓👈
👉👉👉👉👉👉👉👉💓寫作不易,期待大家的關注和點贊💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓關注微信公眾號【技術探界】 💓👈👈👈👈👈👈👈👈
前言
數據源,實際就是數據庫連接池,負責管理數據庫連接,在Springboot中,數據源通常以一個bean的形式存在於IOC容器中,也就是我們可以通過依賴注入的方式拿到數據源,然後再從數據源中獲取數據庫連接。
那麼什麼是多數據源呢,其實就是IOC容器中有多個數據源的bean,這些數據源可以是不同的數據源類型,也可以連接不同的數據庫。
下圖是Springboot中業務線程發起數據庫請求時的一次示意圖。
本文將對多數據源如何加載,如何結合MyBatis使用進行説明,知識點腦圖如下所示。
本文示例代碼如下。
learn-multidatasource
learn-multidatasource-aop
正文
一. 數據源概念
數據源,其實就是數據庫連接池,負責數據庫連接的全生命週期管理。
目前使用較多並且性能較優的有如下幾款數據源。
- TomcatJdbc。TomcatJdbc是Apache提供的一種數據庫連接池解決方案,各方面都還行,各方面也都不突出(均衡大師);
- Druid。Druid是阿里開源的數據庫連接池,是阿里監控系統Dragoon的副產品,提供了強大的可監控性和基於Filter-Chain的可擴展性(BUG 有點多);
- HikariCP。HikariCP是基於BoneCP進行了大量改進和優化的數據庫連接池,是Springboot 2.x版本默認的數據庫連接池,也是速度最快的數據庫連接池(快男)。
二. Springboot加載數據源原理分析
負責完成數據源加載的類叫做DataSourceAutoConfiguration,由spring-boot-autoconfigure包提供,DataSourceAutoConfiguration的加載是基於Springboot的自動裝配機制。
下面先看一下DataSourceAutoConfiguration的部分代碼實現。
@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import(DataSourcePoolMetadataProvidersConfiguration.class)
public class DataSourceAutoConfiguration {
// 省略
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
}
// 省略
}
上述展示出來的代碼,做了兩件和加載數據源有關的事情。
- 將數據源的配置類 DataSourceProperties 註冊到了容器中;
- 將 DataSourceConfiguration 的靜態內部類 Hikari 註冊到了容器中。
先看一下DataSourceProperties的實現,如下所示。
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private ClassLoader classLoader;
private boolean generateUniqueName = true;
private String name;
private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;
// 省略
}
DataSourceProperties中加載了配置在application.yml文件中的spring.datasource.xxx等配置,例如type,url,username和password等都會加載在DataSourceProperties中。
再看一下DataSourceConfiguration的靜態內部類Hikari的實現,如下所示。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
可知Hikari會向容器註冊一個HikariCP的數據源HikariDataSource,同時HikariDataSource也是一個配置類,其會加載application.yml文件中的spring.datasource.hikari.xxx等和HikariCP相關的數據源配置,例如max-lifetime和keep-alive-time等都會加載在HikariDataSource中。
並且創建HikariDataSource的createDataSource() 方法的第一個參數是容器中的DataSourceProperties的bean,所以在創建HikariDataSource時,肯定是需要使用到DataSourceProperties裏面保存的相關配置的,下面看一下DataSourceConfiguration的createDataSource() 方法的實現。
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
DataSourceProperties的initializeDataSourceBuilder() 方法會返回一個DataSourceBuilder,具體實現如下。
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
.url(determineUrl()).username(determineUsername()).password(determinePassword());
}
説明在創建DataSourceBuilder時,會一併設置type,url,username和password等屬性。
知道你不愛看源碼
,如下是Springboot加載數據源原理小結。
- 數據源的 通用配置 會保存在 DataSourceProperties 中。例如url,username和password等配置都屬於通用配置;
- HikariCP 的數據源是 HikariDataSource,HikariCP 相關的配置會保存在 HikariDataSource 中。例如max-lifetime,keep-alive-time等都屬於HiakriCP相關配置;
- 通過 DataSourceProperties 可以創建 DataSourceBuilder;
- 通過 DataSourceBuilder 可以創建具體的數據源。
三. Springboot加載多數據源實現
加載數據源可以分為如下三步。
- 讀取數據源配置信息;
- 創建數據源的 bean;
- 將數據源 bean 註冊到 IOC 容器中。
因此我們可以自定義一個配置類,在配置類中讀取若干個數據源的配置信息,然後基於這些配置信息創建出若干個數據源,最後將這些數據源全部註冊到IOC容器中。
首先application.yml文件內容可以配置如下。
lee:
datasource:
ds1:
max-lifetime: 1600000
keep-alive-time: 90000
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.101.8:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username: root
password: root
pool-name: testpool-1
ds2:
max-lifetime: 1600000
keep-alive-time: 90000
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.101.8:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username: root
password: root
pool-name: testpool-2
然後自定義的配置類如下所示。
@Configuration
public class MultiDataSourceConfig {
@Bean(name = "ds1")
@ConfigurationProperties(prefix = "lee.datasource.ds1")
public DataSource ds1DataSource() {
return new HikariDataSource();
}
@Bean(name = "ds2")
@ConfigurationProperties(prefix = "lee.datasource.ds2")
public DataSource ds2DataSource() {
return new HikariDataSource();
}
}
此時就註冊了兩個HikariDataSource到了IOC容器中。
四. MyBatis整合Springboot原理分析
通常如果要將MyBatis集成到Spring中,需要提供如下的配置類。
@Configuration
@ComponentScan(value = "掃描包路徑")
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(pooledDataSource());
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("Mybatis配置文件名"));
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("映射接口包路徑");
return msc;
}
// 創建一個數據源
private PooledDataSource pooledDataSource() {
PooledDataSource dataSource = new PooledDataSource();
dataSource.setUrl("數據庫URL地址");
dataSource.setUsername("數據庫用户名");
dataSource.setPassword("數據庫密碼");
dataSource.setDriver("數據庫連接驅動");
return dataSource;
}
}
也就是MyBatis如果要集成到Spring中,需要向容器中註冊SqlSessionFactory的bean,以及MapperScannerConfigurer的bean。
因此有理由相信MyBatis整合Springboot的starter包mybatis-spring-boot-starter應該就是在做上述的事情,下面來分析一下mybatis-spring-boot-starter的工作原理。
首先在POM中引入mybatis-spring-boot-starter的依賴,如下所示。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
mybatis-spring-boot-starter會引入mybatis-spring-boot-autoconfigure,看一下mybatis-spring-boot-autoconfigure的spring.factories文件,如下所示。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
所以負責自動裝配MyBatis的類是MybatisAutoConfiguration,該類的部分代碼如下所示。
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
// 省略
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
// 設置數據源
factory.setDataSource(dataSource);
// 省略
return factory.getObject();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 省略
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// 省略
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}
// 省略
}
歸納一下MybatisAutoConfiguration做的事情。
- 將 MyBatis 相關的配置加載到 MybatisProperties 並註冊到容器中。實際就是將application.yml文件中配置的mybatis.xxx相關的配置加載到MybatisProperties中;
- 基於 Springboot 加載的數據源創建 SqlSessionFactory 並註冊到容器中;
- 基於 SqlSessionFactory 創建 SqlSessionTemplate 並註冊到容器中;
- 使用 AutoConfiguredMapperScannerRegistrar 向容器註冊 MapperScannerConfigurer。AutoConfiguredMapperScannerRegistrar實現了ImportBeanDefinitionRegistrar接口,因此可以向容器註冊bean。
所以MybatisAutoConfiguration和我們自己將MyBatis集成到Spring做的事情是一樣的。
- 獲取一個數據源並基於這個數據源創建 SqlSessionFactory 的 bean 並註冊到容器中;
- 創建 MapperScannerConfigurer 的 bean 並註冊到容器中。
五. MyBatis整合Springboot多數據源實現
mybatis-spring-boot-starter是單數據源的實現,本節將對MyBatis整合Springboot的多數據實現進行演示和説明,示例代碼見learn-multidatasource。
首先需要引入相關依賴,POM文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.7.6</version>
</parent>
<groupId>com.lee.learn.multidatasource</groupId>
<artifactId>learn-multidatasource</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
然後提供多數據源的配置,application.yml文件如下所示。
lee:
datasource:
ds1:
max-lifetime: 1600000
keep-alive-time: 90000
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.101.8:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username: root
password: root
pool-name: testpool-1
ds2:
max-lifetime: 1600000
keep-alive-time: 90000
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.101.8:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username: root
password: root
pool-name: testpool-2
現在先看一下基於數據源ds1的MyBatis的配置類,如下所示。
@Configuration
public class MybatisDs1Config {
@Bean(name = "ds1")
@ConfigurationProperties(prefix = "lee.datasource.ds1")
public DataSource ds1DataSource() {
// 加載lee.datasource.ds1.xxx的配置到HikariDataSource
// 然後以ds1為名字將HikariDataSource註冊到容器中
return new HikariDataSource();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory1(@Qualifier("ds1") DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 設置數據源
sqlSessionFactoryBean.setDataSource(dataSource);
// 設置MyBatis的配置文件
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer1(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
// 設置使用的SqlSessionFactory的名字
msc.setSqlSessionFactoryBeanName("sqlSessionFactory1");
// 設置映射接口的路徑
msc.setBasePackage("com.lee.learn.multidatasource.dao.mapper1");
return msc;
}
}
基於數據源ds2的MyBatis的配置類,如下所示。
@Configuration
public class MybatisDs2Config {
@Bean(name = "ds2")
@ConfigurationProperties(prefix = "lee.datasource.ds2")
public DataSource ds2DataSource() {
// 加載lee.datasource.ds2.xxx的配置到HikariDataSource
// 然後以ds2為名字將HikariDataSource註冊到容器中
return new HikariDataSource();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory2(@Qualifier("ds2") DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 設置數據源
sqlSessionFactoryBean.setDataSource(dataSource);
// 設置MyBatis的配置文件
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer2(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
// 設置使用的SqlSessionFactory的名字
msc.setSqlSessionFactoryBeanName("sqlSessionFactory2");
// 設置映射接口的路徑
msc.setBasePackage("com.lee.learn.multidatasource.dao.mapper2");
return msc;
}
}
基於上述兩個配置類,那麼最終com.lee.learn.multidatasource.dao.mapper1路徑下的映射接口使用的數據源為ds1,com.lee.learn.multidatasource.dao.mapper2路徑下的映射接口使用的數據源為ds2。
運行示例工程,調用接口/test/ds1,會有如下的打印字樣。
testpool-1 - Starting...
testpool-1 - Start completed.
説明查詢book表時的連接是從ds1數據源中獲取的,同理調用接口/test/ds2,會有如下打印字樣。
testpool-2 - Starting...
testpool-2 - Start completed.
説明查詢stu表時的連接是從ds2數據源中獲取的。
六. MyBatis整合Springboot多數據源切換
在第五節中,MyBatis整合Springboot多數據源的實現思路是固定讓某些映射接口使用一個數據源,另一些映射接口使用另一個數據源。
本節將提供另外一種思路,通過AOP的形式來指定要使用的數據源,也就是利用切面來實現多數據源的切換,整體的實現思路如下。
- 配置並得到多個數據源;
- 使用一個路由數據源存放多個數據源;
- 將路由數據源配置給 MyBatis 的 SqlSessionFactory;
- 實現切面來攔截對 MyBatis 映射接口的請求;
- 在切面邏輯中完成數據源切換。
那麼現在按照上述思路,來具體實現一下(示例工程learn-multidatasource-aop)。
數據源的配置類如下所示。
@Configuration
public class DataSourceConfig {
@Bean(name = "ds1")
@ConfigurationProperties(prefix = "lee.datasource.ds1")
public DataSource ds1DataSource() {
return new HikariDataSource();
}
@Bean(name = "ds2")
@ConfigurationProperties(prefix = "lee.datasource.ds2")
public DataSource ds2DataSource() {
return new HikariDataSource();
}
@Bean(name = "mds")
public DataSource multiDataSource(@Qualifier("ds1") DataSource ds1DataSource,
@Qualifier("ds2") DataSource ds2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("ds1", ds1DataSource);
targetDataSources.put("ds2", ds2DataSource);
MultiDataSource multiDataSource = new MultiDataSource();
multiDataSource.setTargetDataSources(targetDataSources);
multiDataSource.setDefaultTargetDataSource(ds1DataSource);
return multiDataSource;
}
}
名字為mds的數據源就是所謂的路由數據源,其實現如下所示。
public class MultiDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> DATA_SOURCE_NAME = new ThreadLocal<>();
public static void setDataSourceName(String dataSourceName) {
DATA_SOURCE_NAME.set(dataSourceName);
}
public static void removeDataSourceName() {
DATA_SOURCE_NAME.remove();
}
@Override
public Object determineCurrentLookupKey() {
return DATA_SOURCE_NAME.get();
}
}
我們自定義了一個路由數據源叫做MultiDataSource,其實現了AbstractRoutingDataSource類,而AbstractRoutingDataSource類正是Springboot提供的用於做數據源切換的一個抽象類,其內部有一個Map類型的字段叫做targetDataSources,裏面存放的就是需要做切換的數據源,key是數據源的名字,value是數據源。
當要從路由數據源獲取Connection時,會調用到AbstractRoutingDataSource提供的getConnection() 方法,其實現如下。
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
// 得到實際要使用的數據源的key
Object lookupKey = determineCurrentLookupKey();
// 根據key從resolvedDataSources中拿到實際要使用的數據源
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
在從路由數據源獲取實際使用的數據源時,會經歷如下步驟。
- 通過 determineCurrentLookupKey() 方法拿 key;
- 根據 key 從 resolvedDataSources 中拿到實際使用的數據源。
現在可知每次從路由數據源獲取實際要使用的數據源時,關鍵就在於如何通過determineCurrentLookupKey() 拿到數據源的key,而determineCurrentLookupKey() 是一個抽象方法,所以在我們自定義的路由數據源中對其進行了重寫,也就是從一個ThreadLocal中拿到數據源的key,有拿就有放,那麼ThreadLocal是在哪裏設置的數據源的key的呢,那當然就是在切面中啦。下面一起看一下。
首先定義一個切面,如下所示。
@Aspect
@Component
public class DeterminDataSourceAspect {
@Pointcut("@annotation(com.lee.learn.multidatasource.aspect.DeterminDataSource)")
private void determinDataSourcePointcount() {}
@Around("determinDataSourcePointcount()")
public Object determinDataSource(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
DeterminDataSource determinDataSource = methodSignature.getMethod()
.getAnnotation(DeterminDataSource.class);
MultiDataSource.setDataSourceName(determinDataSource.name());
try {
return proceedingJoinPoint.proceed();
} finally {
MultiDataSource.removeDataSourceName();
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DeterminDataSource {
String name() default "ds1";
}
切點是自定義的註解@DeterminDataSource修飾的方法,這個註解可以通過name屬性來指定實際要使用的數據源的key,同時定義了一個環繞通知,做的事情就是在目標方法執行前將DeterminDataSource註解指定的key放到MultiDataSource的ThreadLocal中,在目標方法執行完畢後,將數據源的key從MultiDataSource的ThreadLocal中移除。
現在已經有路由數據源了,最後一件事情就是將路由數據源給到MyBatis的SessionFactory,配置類MybatisConfig如下所示。
@Configuration
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("mds") DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer1(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setSqlSessionFactoryBeanName("sqlSessionFactory");
msc.setBasePackage("com.lee.learn.multidatasource.dao");
return msc;
}
}
啓動示例工程後,如果調用接口/test/ds1,會有如下的打印字樣。
testpool-1 - Starting...
testpool-1 - Start completed.
説明查詢book表時的連接是從ds1數據源中獲取的,同理調用接口/test/ds2,會有如下打印字樣。
testpool-2 - Starting...
testpool-2 - Start completed.
總結
首先數據源其實就是數據庫連接池,負責連接的全生命週期管理,目前主流的有TomcatJdbc,Druid和HikariCP。
然後Springboot官方的加載數據源實現,實際就是基於自動裝配機制,通過DataSourceAutoConfiguration來加載數據源相關的配置並將數據源創建出來再註冊到容器中。
所以模仿Springboot官方的加載數據源實現,我們可以自己加載多個數據源的配置,然後創建出不同的數據源的bean,再全部註冊到容器中,這樣我們就實現了加載多數據源。
加載完多數據源後我們可以按照如下步驟來使用。
- 通過數據源的的名字也就是 bean 的名字來依賴注入數據源;
- 將不同的數據源設置給不同的 SqlSessionFactory;
- 將不同的 SqlSessionFactory 設置給不同的 MapperScannerConfigurer;
- 最終實現了某一些映射接口使用一個數據源,另一些映射接口使用另一個數據源的效果。
此外還可以藉助AbstractRoutingDataSource來實現數據源的切換,也就是提前將創建好的數據源放入路由數據源中,並且一個數據源對應一個key,然後獲取數據源時通過key來獲取,key的設置通過一個切面來實現,這樣的方式可以在更小的粒度來切換數據源。
大家好,我是半夏之沫 😁😁 一名金融科技領域的JAVA系統研發😊😊
我希望將自己工作和學習中的經驗以最樸實,最嚴謹的方式分享給大家,共同進步👉💓👈
👉👉👉👉👉👉👉👉💓寫作不易,期待大家的關注和點贊💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓關注微信公眾號【技術探界】 💓👈👈👈👈👈👈👈👈