实现SpringBoot项目的多数据源配置的两种方式(dynamic-datasource-spring-boot-starter和自定义注解的方式)
Posted 码农飞哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现SpringBoot项目的多数据源配置的两种方式(dynamic-datasource-spring-boot-starter和自定义注解的方式)相关的知识,希望对你有一定的参考价值。
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。
💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通
❤️ 2. Python爬虫专栏,系统性的学习爬虫的知识点。9.9元买不了吃亏,买不了上当 。python爬虫入门进阶
❤️ 3. Ceph实战,从原理到实战应有尽有。 Ceph实战
❤️ 4. Java高并发编程入门,打卡学习Java高并发。 Java高并发编程入门
😁 5. 社区逛一逛,周周有福利,周周有惊喜。码农飞哥社区,飞跃计划
全网同名【码农飞哥】欢迎关注,个人VX: wei158556
文章目录
1. 简介
最近项目需要配置多数据源,本项目采用的技术是SpringBoot+mybatis-plus+Druid。为了图个方便直接想直接集成dynamic-datasource-spring-boot-starter进行多数据源配置。
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x。
其官方文档的地址是:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
该官方文档分为免费部分和付费部分。付费部分也仅仅只需要29块,29块也不多,就算原作者的支持,个人觉得这29块花的值。
强烈建议使用最新版本,可以在版本记录里查找最新版本
前提
这里默认你已经集成并配置好了mybatis-plus。
集成(第一种实现方式)
仅仅只看基础部分的集成手册是远远不够的。网上好多博客也仅仅只是介绍了基础部分的内容,最终还是达不到想要的效果。本文的集成亲测有效,欢迎读者老爷们阅读。
这里再次强烈建议采用最新版本的dynamic-datasource-spring-boot-starter,具体的版本记录请点击
1. 添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
2. 添加数据源配置
在application.yml文件中将单数据源配置成多数据源,数据源配置的语法结构如下所示:
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx) # 内置加密,使用请查看详细文档
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
此处我的配置实例是:
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master :
url: jdbc:mysql://127.0.0.1:23306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&autoReconnect=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
slave:
url: jdbc:mysql://127.0.0.1:23306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&autoReconnect=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
3. 使用 @DS 切换数据源。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
注解 | 结果 |
---|---|
没有@DS | 使用默认数据源 |
@DS(“dsName”) | dsName可以为组名也可以为具体某个库的名称 |
官方文档里配置到这里就结束了,实际上还远远不够。 |
4. 排除掉DruidDataSourceAutoConfigure
在启动类中需要排除掉DruidDataSourceAutoConfigure.class,就是取消Druid的数据源的自动配置类。
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class, DruidDataSourceAutoConfigure.class)
@MapperScan(basePackages = "com.jay.multidatasource.mapper")
public class MultidatasourceApplication
public static void main(String[] args)
SpringApplication.run(MultidatasourceApplication.class, args);
原理解释(第二种实现方式)
多数据源的配置本质上就是加载多个数据源,并设置默认数据源,给每个数据源设置不同的键值对,当需要切换数据源时就传入目标数据源的键,然后重新设置数据源。下面就做一个简单的演示,就是不使用dynamic-datasource-spring-boot-starter。
1. 定义数据源配置
在application.yml文件中将单数据源配置成多数据源
spring:
datasource:
druid:
db1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:23306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&autoReconnect=true
username: root
password: 123456
db2:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:23306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&autoReconnect=true
username: root
password: 123456
test-on-borrow: true
2. 定义全局的数据源构造类DynamicDataSourceContextHolder
这个类的作用就是管理每个数据源的键,设置当前数据源的键,获取当前数据源的键。
public class DynamicDataSourceContextHolder
private static ThreadLocal<Object> CONTEXT_HOLDER = ThreadLocal.withInitial(() -> DataSourceKey.DCS.getName());
public static List<Object> dataSourceKeys = new ArrayList<Object>();
public static void setDataSourceKey(String key)
CONTEXT_HOLDER.set(key);
public static Object getDataSourceKey()
return CONTEXT_HOLDER.get();
public static void clearDataSourceKey()
CONTEXT_HOLDER.remove();
public static Boolean containDataSourceKey(String key)
return dataSourceKeys.contains(key);
2. 自定义DynamicRoutingDataSource
/**
* 该类继承自 AbstractRoutingDataSource 类,
* 在访问数据库时会调用该类的 determineCurrentLookupKey() 方法获取数据库实例的 key
*/
public class DynamicRoutingDataSource extends AbstractRoutingDataSource
@Override
protected Object determineCurrentLookupKey()
logger.info("current datasource is : ", DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
3. 定义数据源配置类
该类的作用就是初始化数据源DataSource实例,以及初始化SqlSessionFactory实例。这里需要注意的是必须使用MybatisSqlSessionFactoryBean来获取会话工厂SqlSessionFactory,不然的话,baseMapper中的生成动态SQL的方法就不能使用了。
@Configuration
public class DataSourceConfigurer
/**
* 配置数据源
*
* @return
*/
@Bean(name = "db1")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.druid.db1")
public DataSource db1()
return DruidDataSourceBuilder.create().build();
/**
* 配置数据源
*
* @return
*/
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.druid.db2")
public DataSource db2()
return DruidDataSourceBuilder.create().build();
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource()
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>(2);
dataSourceMap.put("db1", db1());
dataSourceMap.put("db2", db2());
dynamicRoutingDataSource.setDefaultTargetDataSource(dcs());
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception
//MybatisPlus使用的是MybatisSqlSessionFactory
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
//此处设置为了解决找不到mapper文件的问题
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception
return new SqlSessionTemplate(sqlSessionFactory());
/**
* 事务
*
* @return
*/
@Bean
public PlatformTransactionManager transactionManager()
return new DataSourceTransactionManager(dynamicDataSource());
4. 自定义注解TargetDataSource
该注解只是作用在方法上,这里默认的数据源是db1.
@Target(ElementType.METHOD, ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource
String value() default "db1";
5. 定义切面DynamicDataSourceAspect
切面顾名思义就是拦击标注TargetDataSource注解的方法,并且根据注解指定的数据源的key切换数据源。
@Aspect
@Component
public class DynamicDataSourceAspect
private Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(targetDataSource))")
public void switchDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource)
if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value().getName()))
logger.error("DataSource [] doesn't exist, use default DataSource []", targetDataSource.value());
else
DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value().getName());
logger.info("Switch DataSource to [] in Method []", DynamicDataSourceContextHolder.getDataSourceKey(), joinPoint.getSignature());
@After("@annotation(targetDataSource))")
public void restoreDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource)
DynamicDataSourceContextHolder.clearDataSourceKey();
logger.info("Restore DataSource to [] in Method []", DynamicDataSourceContextHolder.getDataSourceKey(), joinPoint.getSignature());
6. 使用注解
没有添加注解的方法使用的是默认数据源,当需要使用非默认数据源时,则需要在方法上添加 @TargetDataSource("db2")
注解。需要注意的是,该注解最好添加到xxxMapper类的方法上。
@TargetDataSource("db2")
ClassVO getClassStudent(@Param("open_id") String openId);
总结
本文详细介绍了两种数据源配置的方式
以上是关于实现SpringBoot项目的多数据源配置的两种方式(dynamic-datasource-spring-boot-starter和自定义注解的方式)的主要内容,如果未能解决你的问题,请参考以下文章
基于Maven的SpringBoot项目实现热部署的两种方式