Dynamic+Mybatis-plus动态数据源的实践指南
作者:键盘侠Gu
MyBatis-Plus与动态数据源集成
动态数据源集成的基本概念
传统数据源与动态数据源的区别
在传统的企业级应用中,数据源通常是静态配置的。这意味着,应用启动时会读取配置文件中的数据源信息,并在整个生命周期内使用这一数据源。这种模式适用于单体应用,因为单体应用通常只需要连接到单一数据库。
然而,随着微服务架构的流行,企业开始拆分大型单体应用为一系列小的、自治的微服务,每个微服务通常都有自己的数据库。这导致动态数据源的需求增加,因为一个微服务可能需要根据运行时的条件(如服务调用的上下文),动态切换到不同的数据库。动态数据源的集成使得应用能够灵活地管理和切换多个数据源,而无需重启服务。
动态数据源与传统数据源的最大区别在于其灵活性和适应性,它允许开发者在运行时动态切换数据源,以适应业务需求的变化。这一点在微服务架构中尤为重要,因为它能有效处理服务之间的数据交互和事务管理。
动态数据源集成的需求背景分析
随着业务复杂性的提升,企业对于应用的可扩展性和灵活性要求越来越高。在多租户系统、跨系统数据查询、数据集中处理等场景下,系统需要连接多个数据库,且这些数据库可能分布在不同的物理位置或云环境中。
动态数据源集成正是为了解决这些场景下遇到的问题。在多租户应用中,不同租户的数据存储在不同的数据库实例中,而动态数据源的集成使得系统能够为每个租户切换到正确的数据源,避免了数据错乱。
在跨系统数据交互中,动态数据源能实现系统间的高效数据同步和查询。例如,在微服务架构中,服务A可能需要查询服务B的数据,而服务B使用的数据库与服务A不同。通过动态数据源集成,服务A可以在不关心服务B数据库细节的情况下,透明地访问所需数据。
总之,动态数据源集成的需求背景主要来自于业务的多变性和复杂性,其目标是提升系统的灵活性和维护性,同时降低系统的耦合度,提高数据处理的效率和安全性。
本文介绍Dynamic+Mybatis-plus动态数据源,具体内容如下所示:
在进行配置之前需要引入pom依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.24</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.24</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <version>4.3.1</version> </dependency>
1、首先需要进行配置yml文件,如下
spring: application: name: project-name datasource: dynamic: type: com.alibaba.druid.pool.DruidDataSource # 设置默认的数据源或者数据源组,默认值即为 master primary: master # 严格匹配数据源,默认false,true未匹配到指定数据源时抛异常,false使用默认数据源 strict: true datasource: # 主库数据源 master: type: ${spring.datasource.type} driver-class-name: url: username: password: # 从库数据源 slave: type: ${spring.datasource.type} driver-class-name: url: username: password: druid: inital-size: 10 #最大连接数 max-active: 50 #最小连接数 min-idle: 10 #获取链接等待超时时间 max-wait: 5000 pool-prepared-statements: true #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 max-pool-prepared-statement-per-connection-size: 20 validation-query: SELECT 1 FROM DUAL validation-query-timeout: 20000 test-on-borrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 test-on-return: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 test-while-idle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 time-between-eviction-runs-millis: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 min-evictable-idle-time-millis: 300000 #一个连接在池中最小生存的时间,单位是毫秒 max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒 #StatViewServlet配置。(因为暴露的监控信息比较敏感,支持密码加密和访问ip限定) web-stat-filter: enableed: true stat-view-servlet: enabled: true url-pattern: /druid/* #可以增加访问账号密码【去掉注释就可以】 #login-username: admin #login-password: admin filter: stat: enabled: true # 慢SQL 记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true
2、再配置完之后,添加Druid配置(博主使用的是druid)
package com.guyk.common.config; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Slf4j @Configuration @EnableTransactionManagement(order = 1) // @MapperScan(basePackages = {"com.guyk.dao.mapper"}) public class DruidConfig { @Bean @Primary public DataSource dataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", masterDataSource()); targetDataSources.put("slave", slaveDataSource()); dynamicDataSource.setDefaultTargetDataSource(masterDataSource()); dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } @Bean @ConfigurationProperties("spring.datasource.dynamic.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.dynamic.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } }
3、配置上下文来管理数据源
package com.guyk.common.config; import lombok.extern.slf4j.Slf4j; @Slf4j public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 设置数据源类型 public static void setDataSourceType(String dataSourceType) { log.info("切换数据源: {}", dataSourceType); contextHolder.set(dataSourceType); } // 获取当前线程的数据源类型 public static String getDataSourceType() { return contextHolder.get(); } // 清除当前线程的数据源类型 public static void clearDataSourceType() { String current = getDataSourceType(); contextHolder.remove(); log.info("清除数据源: {}", current); } }
4、添加一个注解,用来在方法、类上使用,进行数据源的切换,并设置默认数据源未master
package com.guyk.common.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { /** * 数据源名称 */ String value() default "master"; }
5、使用AOP切换数据源时,获取@DataSource注解的值,并将数据源类型保存到DataSourceContextHolder中。
package com.guyk.common.config; import com.hhubrain.common.annotation.DataSource; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Slf4j @Aspect @Component @Order(-1) public class DataSourceAspect { /** * 拦截带有@DataSource注解的方法 */ @Around("@annotation(com.hhubrain.common.annotation.DataSource)") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); // 获取注解中的数据源类型 DataSource dataSourceAnnotation = method.getAnnotation(DataSource.class); String dataSourceType = dataSourceAnnotation.value(); // 获取当前线程的数据源类型 String oldDataSource = DataSourceContextHolder.getDataSourceType(); log.info("数据源切换: {} -> {}", oldDataSource, dataSourceType); // 切换数据源 DataSourceContextHolder.setDataSourceType(dataSourceType); try { return point.proceed(); } finally { // 还原数据源 if (oldDataSource != null) { DataSourceContextHolder.setDataSourceType(oldDataSource); log.info("恢复数据源: {}", oldDataSource); } else { DataSourceContextHolder.clearDataSourceType(); // 清除线程数据源 log.info("清除数据源上下文"); } } } }
6、在启动类上排除DataSourceAutoConfiguration.class
到此这篇关于Dynamic+Mybatis-plus动态数据源的文章就介绍到这了,更多相关Dynamic Mybatis-plus数据源内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!