本文主要讲解springboot +mybatisplus + druid 实现多数据源配置功能
一些必要的准备及代码说明
SpringBoot :
Spring Boot是一个基于Spring框架的开源Java开发框架,旨在简化Spring应用程序的开发、配置和部署。它提供了一种快速、敏捷的方式来构建独立的、生产级别的Spring应用程序,同时还提供了许多开箱即用的功能和工具,如自动配置、内嵌式Web容器、健康检查、监控和管理等。
Mybatis-Plus:
Mybatis-Plus是一个基于Mybatis的增强工具,它在Mybatis的基础上扩展了许多实用的功能,可以帮助开发人员更加高效地完成数据库操作,提高开发效率和代码质量。
Druid:
Druid是一个高性能的数据库连接池,它可以与各种关系型数据库进行集成,如MySQL、Oracle、PostgreSQL等。Druid提供了可扩展、高可用、高性能的数据库连接管理功能,可以帮助开发人员更加高效地完成数据库操作。
有了基本的理解 接下来我们用这些组合实现多数据源功能
加入一些必要的依赖:
这里是引用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring-boot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
多数据源的一个简单yml配置:
spring:
datasource:
druid:
initial-size: 5
min-idle: 5
max-active: 5
db1:
url: jdbc:mysql://localhost:6030/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
db2:
url: jdbc:mysql://localhost:6030/demo1?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
在项目启动类中排除druid属性默认加载(也可以直接在配置中加载 我的项目配置没有成功就直接用这种方式了)
不加这个多数据源可能无法都加载出来
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
添加mybatisplus的多数据源配置
@Configuration
public class MybatisPlusConfig {
/** mybatis 3.4.1 pagination config start ***/
@Bean
public PaginationInterceptor paginationInterceptor() {
// return new PaginationInterceptor();
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setDialectType("postgresql");
return paginationInterceptor;
}
/**
* SQL执行效率插件(优化sql的执行效率 不需要可以不加)
*
*/
@Bean
@Profile({"dev","qa"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1000);
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
/**
* 多数据源的创建 有几个就写几个
*/
@Bean(name = "db1")
@ConfigurationProperties(prefix = "spring.datasource.druid.db1" )
public DataSource db1() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.druid.db2" )
public DataSource db2() {
return DruidDataSourceBuilder.create().build();
}
/**
* 动态数据源配置
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("db1") DataSource db1, @Qualifier("db2") DataSource db2) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map< Object, Object > targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceEnum.DB1.getValue(), db1);
targetDataSources.put(DataSourceEnum.DB2.getValue(), db2);
//添加数据源
dynamicDataSource.setTargetDataSources(targetDataSources);
//设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(db1);
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(db1(),db2()));
//sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*/*Mapper.xml"));
MybatisConfiguration configuration = new MybatisConfiguration();
//configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setPlugins(new Interceptor[]{
//PerformanceInterceptor(),OptimisticLockerInterceptor()
paginationInterceptor()
//添加分页功能
});
//sqlSessionFactory.setGlobalConfig(globalConfiguration());
return sqlSessionFactory.getObject();
}
}
枚举:
public enum DataSourceEnum {
DB1("db1"),DB2("db2");
private String value;
DataSourceEnum(String value){this.value=value;}
public String getValue() {
return value;
}
}
自定义多数据源切换:
/**
* 自定义多数据源切换
* @author zzh
* @date 2023/4/28 15:19:21
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSource {
DataSourceEnum value() default DataSourceEnum.DB1;
}
切面处理类:
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.taosdata.example.mybatisplusdemo.datasource.annotation.DataSource)"
+ "|| @within(com.taosdata.example.mybatisplusdemo.datasource.annotation.DataSource)")
public void dsPc() {
}
@Before("dsPc() && @annotation(dataSource)")
public void doBefore(DataSource dataSource) {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().getValue());
}
@After("dsPc()")
public void doAfter() {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
driud配置类 配置启动视图和过滤规则
@Configuration
public class DruidConfiguration {
@Bean
public ServletRegistrationBean startViewServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
// IP白名单
servletRegistrationBean.addInitParameter("allow","127.0.0.1");
// IP黑名单(共同存在时,deny优先于allow)
servletRegistrationBean.addInitParameter("deny","127.0.0.1");
//控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername","admin");
servletRegistrationBean.addInitParameter("loginPassword","123456");
//是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable","false");
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean statFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
//忽略过滤的格式
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
动态数据源决策
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* @description: 动态数据源决策
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
String dataSourceType = DynamicDataSourceContextHolder.getDataSourceType();
return dataSourceType;
}
}
多数据源上下文
public class DynamicDataSourceContextHolder {
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType) {
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
至此,一个非常简单的多数据源配置就基本搞定了。