springboot实现多数据源配置(Druid/Hikari)

article/2025/9/26 4:51:41

使用springboot+mybatis-plus+(Druid/Hikari)实现多数据源配置

操作步骤:

  1. 引入相应的maven坐标
  2. 编写mybatis配置,集成mybatis或mybatis-plus(如果已集成可跳过)
  3. 编写数据源配置类
  4. 编写注解,并通过aop进行增强(编写数据源切换代码)
  5. 类或方法中使用注解,对数据源进行切换

第一步:引入需要相应maven坐标

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- aop注解实现aspectjweaver依赖 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency><!-- mysql-plus 依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.0</version></dependency><!-- Mysql驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--阿里数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.14</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

第二步:编写mybatis-plus配置集成

server:port: 8000spring:datasource:driver-class-name: com.mysql.cj.jdbc.Drivermaster:url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=trueusername: rootpassword: 123456slaver:# 从数据源开关/默认关闭enabled: falseurl: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=trueusername: rootpassword: 123456druid:# 初始连接数initial-size: 5# 最小连接池数量min-idle: 10# 最大连接池数量max-active: 20# 获取连接等待超时时间max-wait: 60000
mybatis-plus:mapper-locations: classpath*:mapper/*Mapper.xmllogging:level:com.example.demo: debug

以下是集成mp相关的代码,不需要可直接跳过!!!(以user表为例)

userMapper

package com.example.demo.datesource.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.datesource.domain.User;import java.util.List;public interface UserService extends IService<User> {List<User> getUserList();List<User> getUserListByXml();List<User> testSlave();
}

userMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.datesource.mapper.UserMapper"><select id="getUserListByXml" resultType="com.example.demo.datesource.domain.User">select * from user</select>
</mapper>

userService

package com.example.demo.datesource.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.datesource.domain.User;import java.util.List;public interface UserService extends IService<User> {List<User> getUserList();List<User> getUserListByXml();List<User> testSlave();
}

userServiceImpl

package com.example.demo.datesource.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.datesource.annotation.UseDataSource;
import com.example.demo.datesource.domain.User;
import com.example.demo.datesource.enums.DataSourceType;
import com.example.demo.datesource.mapper.UserMapper;
import com.example.demo.datesource.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic List<User> getUserList() {return list();}@Overridepublic List<User> getUserListByXml(){return userMapper.getUserListByXml();}@Overridepublic List<User> testSlave() {return list();}
}

User

package com.example.demo.datesource.domain;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("user")
public class User {@TableIdprivate Long id;private String name;private String sex;private int age;
}

mybatis-plus 集成完毕!

第三步:编写数据源的配置类(例:Druid、Hikari)

一、@ConfigurationProperties要和配置文件中的配置对应上

(1)Druid:
package com.example.demo.datesource.config;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.example.demo.datesource.config.properties.DruidPropertiesConfig;
import com.example.demo.datesource.datasource.DynamicDataSource;
import com.example.demo.datesource.enums.DataSourceType;
import com.example.demo.datesource.utils.SpringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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 javax.sql.DataSource;
import javax.swing.*;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DruidConfig {@Bean@ConfigurationProperties("spring.datasource.master")public DataSource masterDataSource(DruidPropertiesConfig propertiesConfig){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return propertiesConfig.setDataSourceProperties(dataSource);}@Bean@ConfigurationProperties("spring.datasource.slaver")@ConditionalOnProperty(prefix = "spring.datasource.slaver", name = "enabled", havingValue = "true")public DataSource slaverDataSource(DruidPropertiesConfig propertiesConfig){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return propertiesConfig.setDataSourceProperties(dataSource);}@Bean@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource){Map<Object,Object> targetDataSource = new HashMap<>();targetDataSource.put(DataSourceType.MASTER.name(),masterDataSource);setDataSource(targetDataSource,DataSourceType.SLAVE.name(),"slaverDataSource");return new DynamicDataSource(masterDataSource,targetDataSource);}public void setDataSource(Map<Object,Object> targetDataSource,String sourceName,String beanName){try {DataSource dataSource = SpringUtils.getBean(beanName);targetDataSource.put(sourceName,dataSource);}catch (Exception e){}}}

增加DruidPropertiesConfig类,用于给数据源配置上配置文件对应的属性

package com.example.demo.datesource.config.properties;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;@Configuration
public class DruidPropertiesConfig {@Value("${spring.datasource.druid.initial-size}")private int initialSize;@Value("${spring.datasource.druid.min-idle}")private int minIdle;@Value("${spring.datasource.druid.max-active}")private int maxActive;@Value("${spring.datasource.druid.max-wait}")private int maxWait;public DruidDataSource setDataSourceProperties(DruidDataSource dataSource){dataSource.setInitialSize(this.initialSize);dataSource.setMinIdle(this.minIdle);dataSource.setMaxActive(this.maxActive);dataSource.setMaxWait(this.maxWait);return dataSource;}}
(2)Hikari:配置上基本和druid相同,只需把配置类改为如下且配置文件的“url”改为“jdbc-url”即可

在这里插入图片描述
在这里插入图片描述

二、增加SpringUtils类,用于读取spring中的bean

package com.example.demo.datesource.utils;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;@Component
public class SpringUtils implements BeanFactoryPostProcessor {private static ConfigurableListableBeanFactory beanFactory;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {SpringUtils.beanFactory = configurableListableBeanFactory;}public static <T> T getBean(String name){return (T) beanFactory.getBean(name);}}

三、添加DynamicDataSource类

(1)此类是用来配置动态数据源的关键。该类继承spring的抽象类AbstractRoutingDataSource,通过这个类可以实现动态数据源切换。其中维护着默认数据源(defaultTargetDataSource)和数据源列表(targetDataSources),通过afterPropertiesSet()方法对数据源列表进行解析以及设置数据源。
程序每次对数据库发起连接时,都会访问到AbstractRoutingDataSource的getConnection()方法,此方法会调用determineCurrentLookupKey的相应实现,此处实现为获取线程变量。

package com.example.demo.datesource.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.Map;public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources){super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}
}

(2)添加DynamicDataSourceContextHolder类,此类用于切换数据源,根据ThreadLocal做多线程数据隔离,每一次切换都能保证不影响其他线程的正常运行

package com.example.demo.datesource.datasource;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class DynamicDataSourceContextHolder {public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);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();}/*** 清空数据源变量,此方法可调动gc对线程进行清除*/public static void clearDataSourceType(){CONTEXT_HOLDER.remove();}}

AbstractRoutingDataSource切换线程变量来切换数据源源码。
此处就是获取之前在DynamicDataSourceContextHolder中set到线程中的数据源名,通过数据源名获取维护的数据源列表中对应的数据源

在这里插入图片描述

第四步:使用

大致思路:定义一个切换数据源注解,通过注解aop的形式对数据源进行切换,在类或方法中使用注解,设置对应的数据源名称以此来达到数据源切换

一、定义一个切换数据源注解

package com.example.demo.datesource.annotation;import com.example.demo.datesource.enums.DataSourceType;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UseDataSource {public DataSourceType dataSource() default DataSourceType.MASTER;}

二、编写aop代码

@within:此注解可以作用于类中的所有方法,如果@UseDataSource放到类上,此类下的所有的方法都会被当做切点拦截
@Order(1):提升组件注册到spring中的优先级
point.proceed():原来执行的程序,也就是原请求,想要做环绕增强就再这周围写就可以了

package com.example.demo.datesource.aspectj;import com.example.demo.datesource.annotation.UseDataSource;
import com.example.demo.datesource.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import javax.sql.DataSource;
import java.util.Objects;@Aspect
@Order(1)
@Component
public class DataSourceAspect {@Pointcut("@annotation(com.example.demo.datesource.annotation.UseDataSource) || @within(com.example.demo.datesource.annotation.UseDataSource)")public void pointCut(){}@Around("pointCut()")public Object around(ProceedingJoinPoint point) throws Throwable{UseDataSource useDataSource = getDataSource(point);if(Objects.nonNull(useDataSource)){DynamicDataSourceContextHolder.setDataSourceType(useDataSource.dataSource().name());}try {return point.proceed();}finally {//清空threadlocalMap的entry。避免内存泄漏DynamicDataSourceContextHolder.clearDataSourceType();}}public UseDataSource getDataSource(ProceedingJoinPoint point){MethodSignature signature = (MethodSignature) point.getSignature();UseDataSource annotation = AnnotationUtils.findAnnotation(signature.getMethod(), UseDataSource.class);if(Objects.nonNull(annotation)){return annotation;}return AnnotationUtils.findAnnotation(signature.getDeclaringType(),UseDataSource.class);}}

三、编写注解到类或方法上

在这里插入图片描述

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

至此,数据源切换成功!!!

另外,如果需要增加新的数据源需要的操作:
1、启动配置文件中增加新的数据源参数
2、数据源配置类中,新增配置源bean,并且把数据源set到targetDataSource中
3、DataSourceType枚举类中,添加新数据源的枚举
如图所示:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


http://chatgpt.dhexx.cn/article/UphURodn.shtml

相关文章

HikariDataSource

DataSourceConfiguration 配置类&#xff0c;springBoot默认采用HikariDataSource /*** Hikari DataSource configuration.*/Configuration(proxyBeanMethods false)ConditionalOnClass(HikariDataSource.class)ConditionalOnMissingBean(DataSource.class)ConditionalOnPro…

Hikari

HikariCP 1 简介 数据库连接池就是在程序初始化的时候&#xff0c;预先创建一定数量的数据库连接对象&#xff0c;当后续需要数据库连接的时候&#xff0c;如果此时有连接未被使用&#xff0c;那么他就可以直接使用已经创建好的连接&#xff0c;不需要再重新创建新的连接&…

关于Hikari连接池的源码理解与配置使用

关于Hikari连接池的源码理解与配置使用 1. 连接池初始化 1.1 初始化连接池 对于应用者来说&#xff0c;我们构造一个线程池就是构造一个HikariDataSource。 重点看一下获取连接以及相关连接管理的流程。 public Connection getConnection() throws SQLException {if (this…

Hikari 讲解

前言 现在已经有很多公司在使用HikariCP了&#xff0c;HikariCP还成为了SpringBoot默认的连接池&#xff0c;伴随着SpringBoot和微服务&#xff0c;HikariCP 必将迎来广泛的普及。 下面带大家从源码角度分析一下HikariCP为什么能够被Spring Boot 青睐&#xff0c;文章目录如下…

SpringBoot系列十八:整合Hikari

Hikari是一款非常强大&#xff0c;高效&#xff0c;并且号称“史上最快连接池”。并且在springboot2.0之后&#xff0c;采用的默认数据库连接池就是Hikari。不需要引入依赖&#xff0c;已经在SpringBoot中包含了。  GitHub地址&#xff1a;https://github.com/brettwooldridge…

Android开发之RelativeLayout

文章目录 常见属性根据父容器定位根据兄弟容器定位 实例根据父容器定位根据兄弟组件定位 通用属性设置组件与父容器的边距设置父容器与组件的边距 常见属性 根据父容器定位 layout_alignParentStart 左对齐 layout_alignParentEnd 右对齐 layout_alignParentTop 顶部对齐 lay…

Android RelativeLayout布局

1. RelativeLayout类 相对布局&#xff08;RelativeLayout&#xff09;将子视图以相对位置显示。默认显示在父视图的左上角。 layout_alignParentTop&#xff0c;父视图的上边layout_alignParentBottom&#xff0c;父视图的下边layout_alignParentLeft&#xff0c;父视图的左…

android 继承relativelayout,Android开发中RelativeLayout相对布局

Android开发中RelativeLayout相对布局 RelativeLayout布局是Android界面布局中应用最广也最强大的一种布局&#xff0c;其不只十分灵活&#xff0c;能够解决开发中各类界面布局需求&#xff0c;同时也很方便了解决了多屏幕尺寸的适配问题。在iOS开发中&#xff0c;Autolayout技…

RelativeLayout实现居中偏下x距离引发的小问题

UE想实现一个简单的效果&#xff0c;某个控件在屏幕水平线下方50dp。由于受限于项目历史布局&#xff08;RelativeLayout&#xff09;和一套动态化设置控件位置的方案&#xff0c;竟然遇到了一点问题。&#xff08;在这里还是喊一声&#xff1a;ConstraintLayout最香&#xff0…

RelativeLayout布局

RelativeLayout布局是相对布局&#xff0c;如果RelativeLayout中再包含两个RelativeLayout&#xff0c;不会像LinearLayout一样&#xff0c;宽高一样的话会重叠在一起 将红色布局放到右上角 常见属性 根据父容器定位 layout_alignParentLeft 左对齐 layout_alignParentRig…

RelativeLayout圆角处理

RelativeLayout圆角处理以后&#xff0c;可以变相对子view进行圆角处理&#xff0c;如ImageView&#xff0c;VideoView等 RoundRelativeLayout具体实现 比较简单&#xff0c;只需要在初始化时设置一下layout的ViewOutlineProvider&#xff0c;方便起见&#xff0c;这里写死rad…

RelativeLayout(相对布局)的基本使用

RelativeLayout又称作相对布局&#xff0c;也是一种非常常用的布局。和LinearLayout的排列规则不同&#xff0c;RelativeLayout显得更加随意一些&#xff0c;它可以通过相对定位的方式让控件出现在布局的任何位置。也正因为如此&#xff0c;RelativeLayout中的属性非常多&#…

RelativeLayout相对布局详解

RelativeLayout相对布局在Android UI开发中也应用比较多&#xff0c;虽然它不及LinearLayout使用方便&#xff0c;但某些场景中使用RelativeLayout也是一种很不错的选择。在官网上介绍RelativeLayout是一个视图组&#xff0c;在相对位置显示子视图。RelativeLayout是用于设计用…

Android相对布局(RelativeLayout)

Android相对布局(RelativeLayout) 备注&#xff1a;这里的视图和元素是等同的概念。 RelativeLayout是一个允许子视图相对于其他兄弟视图或是父视图显示的视图组(通过ID指定)。每个视图的位置能够指定它相对于兄弟(比如在其他视图的左边或是下边)或是父视图(这里是指相对布局容…

【Android】相对布局(RelativeLayout)最全解析

【Android】相对布局&#xff08;RelativeLayout&#xff09;最全解析 一、相对布局&#xff08;RelativeLayout&#xff09;概述二、根据父容器定位三、根据兄弟控件定位 一、相对布局&#xff08;RelativeLayout&#xff09;概述 相对布局&#xff08;RelativeLayout&#x…

Flutter ListView详解

ListView详解 ListView常用构造ListViewListView 默认构建效果 ListView ListTileListTile 属性ListTile 使用效果 ListView builderbuilder属性详细介绍分析几个比较难理解的属性效果builder模式来设置分割线 ListView separatedseparatorBuilderseparated设置分割线效果 List…

UE4 ListView

UE4-ListView UE4ListView和U3D的思路不太一样&#xff0c;详细了解之后发现UE4的ListView还是蛮先进的&#xff0c;直接就实现了逻辑和显示分离&#xff0c;提高效率&#xff0c;相对的&#xff0c;他的用法也比Unity的ListView绕一些。举例&#xff0c;一个ListView如果设置…

Android 控件 —— ListView

ListView 的简单用法 在布局中加入 ListView 控件还算简单&#xff0c;先为 ListView 指定一个 id&#xff0c;然后将宽度和高度都设置为 match_parent&#xff0c;这样 ListView 就占满了整个布局的空间 <LinearLayout xmlns:android"http://schemas.android.com/ap…

ListView 组件

简介&#xff1a; ListView是最常用的可滚动组件之一 有三种构建方式&#xff1a; ListViewListView.builderListView.separated 主要参数说明&#xff1a; scrollDirection: Axis.horizontal 水平列表Axis.vertical 垂直列表 padding: 内边距 resolve: 组件反向排序 childr…

4.ListView

ListView 文章目录 ListView一、什么是ListView二、ListView入门1.ListView核心类2.代码编写步骤 三、ListView优化四、把复杂界面(通过xml文件实现)显示到ListView上1.View的静态方法2.获取LayoutInflater对象 五、SimpleAdapter & ArrayAdapter的使用1.ArrayAdapter2.Sim…