SpringBoot的多数据源配置主要有两种,一种是基于springboot+mybatis
,一种是基于springboot+druid+mybatisplus
方式。
主要依赖:
pom坐标:
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
server:
port: 8080 # 启动端口
spring:
datasource:
db1: # 数据源1
jdbc-url: jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
db2: # 数据源2
jdbc-url: jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
注意事项:各个版本的 springboot 配置 datasource 时参数有所变化,例如低版本配置数据库 url 时使用 url 属性,高版本使用 jdbc-url 属性,请注意区分。
@Configuration
@MapperScan(basePackages = "com.example.multipledatasource.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
public class DataSourceConfig1 {
@Primary // 表示这个数据源是默认数据源, 这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源)
@Bean("db1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.db1") //读取application.yml中的配置参数映射成为一个对象
public DataSource getDb1DataSource(){
return DataSourceBuilder.create().build();
}
@Primary
@Bean("db1SqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致)
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/db1/*.xml"));
return bean.getObject();
}
@Primary
@Bean("db1SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "com.example.multipledatasource.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory")
public class DataSourceConfig2 {
@Bean("db2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource getDb1DataSource(){
return DataSourceBuilder.create().build();
}
@Bean("db2SqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/db2/*.xml"));
return bean.getObject();
}
@Bean("db2SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
}
主要依赖:
pom坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
server:
port: 8080
spring:
datasource:
dynamic:
primary: db1 # 配置默认数据库
datasource:
db1: # 数据源1配置
url: jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
db2: # 数据源2配置
url: jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
durid:
initial-size: 1
max-active: 20
min-idle: 1
max-wait: 60000
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 去除druid配置
注意事项:DruidDataSourceAutoConfigure会注入一个DataSourceWrapper,其会在原生的spring.datasource下找 url, username, password 等。动态数据源 URL 等配置是在 dynamic 下,因此需要排除,否则会报错。
排除方式有两种,一种是上述配置文件排除,还有一种可以在项目启动类排除:
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@DS 可以注解在方法上和类上,同时存在方法注解优先于类上注解。
注解在 service 实现或 mapper 接口方法上,不要同时在 service 和 mapper 注解。
@DS("db2")
public interface UserMapper extends BaseMapper<User> {
}
@Service
@DS("db2")
public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements IModelService {}
@Select("SELECT * FROM user")
@DS("db2")
List<User> selectAll();
Map<Object, Object> targetDataSources
保存了所有可能的数据源,key为数据库的key
,value
为DataSource
对象或字符串形式的连接信息Object defaultTargetDataSource
保存了默认的数据源,用于找不到具体的数据源时使用afterPropertiesSet()
方法targetDataSources
数据源信息成<key,DataSource>
的形式,保存在Map<Object, DataSource> resolvedDataSources
中defaultTargetDataSource
中的默认数据源信息解析成 DataSource
对象保存在 DataSource resolvedDefaultDataSource
中determineCurrentLookupKey()
提供给子类重写,指定当前线程使用的具体的数据源的keydetermineTargetDataSource()
根据 determineCurrentLookupKey()
方法返回的key 返回数据源DataSouce
对象,若没有,则使用默认数据源对象getConnection()
根据determineTargetDataSource()
返回的数据源,与其建立连接。创建一个类用于操作 ThreadLocal
,主要是通过get,set,remove
方法来获取、设置、删除当前线程对应的数据源。
public class DataSourceContextHolder {
//此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的初始化变量的副本。
private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();
/**
* 设置数据源
*/
public static void setDataSource(String dataSourceName){
DATASOURCE_HOLDER.set(dataSourceName);
}
/**
* 获取当前线程的数据源
*/
public static String getDataSource(){
return DATASOURCE_HOLDER.get();
}
/**
* 删除当前数据源
*/
public static void removeDataSource(){
DATASOURCE_HOLDER.remove();
}
}
定义一个动态数据源继承 AbstractRoutingDataSource
,通过determineCurrentLookupKey
方法与上述实现的ThreadLocal
类中的get
方法进行关联,实现动态切换数据源。
/**
* @description: 实现动态数据源,根据AbstractRoutingDataSource路由到不同数据源中
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource defaultDataSource,Map<Object, Object> targetDataSources){
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(targetDataSources);
}
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
master:
url: jdbc:mysql://xxxxxx:3306/test1?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
username: xxxx
password: xxxx
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
username: xxxx
password: xxxx
driver-class-name: com.mysql.cj.jdbc.Driver
配置类
通过配置类,将配置文件中的配置的数据库信息转换成数据源,并添加到DynamicDataSource
中,同时通过@Bean
将DynamicDataSource
注入Spring中进行管理。
/**
* @description: 设置数据源
**/
@Configuration
public class DateSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource slaveDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource createDynamicDataSource(){
Map<Object,Object> dataSourceMap = new HashMap<>();
DataSource defaultDataSource = masterDataSource();
dataSourceMap.put("master",defaultDataSource);
dataSourceMap.put("slave",slaveDataSource());
return new DynamicDataSource(defaultDataSource,dataSourceMap);
}
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DS {
String value() default "master";
}
@Aspect
@Component
@Slf4j
public class DSAspect {
@Pointcut("@annotation(com.test.dynamic_datasource.dynamic.aop.DS)")
public void dynamicDataSource(){}
@Around("dynamicDataSource()")
public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature)point.getSignature();
Method method = signature.getMethod();
DS ds = method.getAnnotation(DS.class);
if (Objects.nonNull(ds)){
DataSourceContextHolder.setDataSource(ds.value());
}
try {
return point.proceed();
} finally {
DataSourceContextHolder.removeDataSource();
}
}
}
使用@Around
,通过ProceedingJoinPoint
获取注解信息,拿到注解传递值,然后设置当前线程的数据源。
使用@DS("master“")
和@DS("slave")
可实现自由切换数据源。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务