Springboot动态数据源配置

1. 数据准备

create database if not exists `ds1`;
create database if not exists `ds3`;
  • ds1
/*Navicat Premium Data TransferSource Server         : localhost_3306Source Server Type    : MySQLSource Server Version : 80030Source Host           : localhost:3306Source Schema         : ds1Target Server Type    : MySQLTarget Server Version : 80030File Encoding         : 65001Date: 05/03/2024 16:45:53
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户名',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` (`id`, `username`) VALUES (1, 'wms');
COMMIT;SET FOREIGN_KEY_CHECKS = 1;
  • ds3
/*Navicat Premium Data TransferSource Server         : localhost_3306Source Server Type    : MySQLSource Server Version : 80030Source Host           : localhost:3306Source Schema         : ds3Target Server Type    : MySQLTarget Server Version : 80030File Encoding         : 65001Date: 05/03/2024 16:45:02
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户名',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` (`id`, `username`) VALUES (1, 'zhangsan');
COMMIT;SET FOREIGN_KEY_CHECKS = 1;

2. 版本

  • SprintBoot:2.7.11
  • Mybatis-Plus:3.5.5
  • MySQL:8.0.30

3. 引入依赖

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot.version>2.7.11</spring-boot.version><lombok.version>1.18.30</lombok.version><mysql.version>8.0.33</mysql.version><mybatis-plus.version>3.5.5</mybatis-plus.version>
</properties><!-- web -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions>
</dependency><!-- log4j2 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId>
</dependency><!-- jdbc -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency><!-- spring-boot-starter-validation -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency><!-- spring-boot-starter-aop -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency><!-- mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency><!-- mybatis-plus-boot-starter -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId>
</dependency><!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!-- mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!-- mybatis-plus-boot-starter --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies>
</dependencyManagement>

4. 编码

  • 服务入口
/*** 元数据服务** @author Wxf* @since 2024-03-04 09:13:15**/
@MapperScan(basePackages = "com.wxf.metadata.mapper"
)
@SpringBootApplication(scanBasePackages = "com.wxf.metadata"
)
public class MetadataApplication {public static void main(String[] args) {SpringApplication.run(MetadataApplication.class, args);}
}
  • application.yml
spring:profiles:active: dev
  • application-dev.yml
server:port: 8099shutdown: gracefulspring:application:name: metadata-servicedatasource:jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&useIPv6=falseusername: rootpassword: rootdriverClassName: com.mysql.cj.jdbc.Driverhikari:maximum-pool-size: 500max-lifetime: 18000000minimum-idle: 30connection-timeout: 30000connection-test-query: SELECT 1pool-name: HiKariDataSourcetype: com.zaxxer.hikari.HikariDataSourceidle-timeout: 180000auto-commit: truejackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8# Mybatis Plus 配置
mybatis-plus:mapper-locations: classpath:/mapper/*.xmltype-aliases-package: com.dcits.metadata.entitycheck-config-location: false # 是否xml是否存在global-config:db-config:id-type: assign_id # 默认使用雪花IDlogic-delete-field: deleted # 是否逻辑删除logic-delete-value: 1  # 逻辑删除:是logic-not-delete-value: 0 # 逻辑删除:否configuration:map-underscore-to-camel-case: true # 启用驼峰命名default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerlog-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印日志# 日志
logging:config: classpath:log4j2-spring.xmlcharset:file: UTF-8
  • log4j2-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5"><!--变量配置--><Properties><!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--><!-- %logger{36} 表示 Logger 名字最长36个字符 --><property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/><!-- 定义日志存储的路径 --><property name="FILE_PATH" value="${env:LOG_DIR:-logs/metadata-service}"/><property name="FILE_NAME" value="metadata-service"/></Properties><appenders><console name="Console" target="SYSTEM_OUT"><!--输出日志的格式--><PatternLayout pattern="${LOG_PATTERN}"/><!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/></console><!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log"filePattern="${FILE_PATH}/backup/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile><!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/${FILE_NAME}-warn.log"filePattern="${FILE_PATH}/backup/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile><!-- 这个会打印出所有的debug及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileDebug" fileName="${FILE_PATH}/${FILE_NAME}-debug.log"filePattern="${FILE_PATH}/backup/${FILE_NAME}-DEBUG-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile><!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log"filePattern="${FILE_PATH}/backup/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="${LOG_PATTERN}"/><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour--><TimeBasedTriggeringPolicy interval="1"/><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--><DefaultRolloverStrategy max="15"/></RollingFile></appenders><!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。--><!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效--><loggers><root level="DEBUG"><appender-ref ref="Console"/><appender-ref ref="RollingFileInfo"/><appender-ref ref="RollingFileWarn"/><appender-ref ref="RollingFileError"/><appender-ref ref="RollingFileDebug"/></root></loggers></configuration>
  • Mybatis-Plus配置

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Mybatis Plus 配置** @author Wxf* @since 2024-01-22 10:59:28**/
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();// 设置数据库类型为mysql// 如果配置多个插件,切记分页最后添加// 如果有多数据源可以不配具体类型 否则都建议配上具体的DbTypemybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());// 乐观锁插件mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}//    @Beanpublic MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {return properties -> {GlobalConfig globalConfig = properties.getGlobalConfig();globalConfig.setBanner(false);MybatisConfiguration configuration = new MybatisConfiguration();// 配置枚举configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);properties.setGlobalConfig(globalConfig);};}
}
  • 动态数据源对象
/*** 动态数据源** @author Wxf* @since 2024-03-05 10:47:30**/
public class DynamicDatasource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DatasourceContextHolder.getDataSource();}
}
  • 缓存数据源,供动态注入

/*** 数据源缓存区** @author Wxf* @since 2024-03-05 10:56:45**/
public class DatasourceMapCache {private static final Map<Object, Object> DATA_SOURCE_MAP = new ConcurrentHashMap<>(16);public static Map<Object, Object> getDataSourceMap() {return DATA_SOURCE_MAP;}public static void refreshDataSource(String datasourceKey, DataSource dataSource) {// 将新增数据源放入到Map中,作为目标数据源DATA_SOURCE_MAP.put(datasourceKey, dataSource);// 获取到动态数据源对象DynamicDatasource dynamicDatasource = SpringApplicationContext.getBean(DynamicDatasource.class);// 设置目标数据源dynamicDatasource.setTargetDataSources(DATA_SOURCE_MAP);// 刷新并输出化新增数据源dynamicDatasource.afterPropertiesSet();}public static void removeDataSource(String datasourceKey) {DATA_SOURCE_MAP.remove(datasourceKey);}
}
  • 数据源切换器
/*** 数据源holder** @author Wxf* @since 2024-03-05 10:48:31**/
public class DatasourceContextHolder {private static final ThreadLocal<String> DATA_SOURCE_THREAD_LOCAL = ThreadLocal.withInitial(() -> "defaultDatasource");public static String getDataSource() {return DATA_SOURCE_THREAD_LOCAL.get();}public static void setDataSource(String datasourceKey) {DATA_SOURCE_THREAD_LOCAL.set(datasourceKey);}public static void remove() {DATA_SOURCE_THREAD_LOCAL.remove();}}
  • 数据源配置

import javax.sql.DataSource;
import java.util.Map;/*** 数据源配置** @author Wxf* @since 2024-03-05 11:04:39**/
@Configuration
public class DatasourceConfig {@Beanpublic JdbcTemplate jdbcTemplate(@Qualifier("dynamicDatasource") DynamicDatasource dynamicDatasource) {return new JdbcTemplate(dynamicDatasource);}// 构建默认数据源@Bean(name = "defaultDatasource")@ConfigurationProperties(prefix = "spring.datasource")public DataSource dataSource() {return DataSourceBuilder.create().build();}// 动态数据源@Bean("dynamicDatasource")public DynamicDatasource dynamicDatasource(@Qualifier("defaultDatasource") DataSource dataSource) {DynamicDatasource dynamicDatasource = new DynamicDatasource();// 设置默认数据源dynamicDatasource.setDefaultTargetDataSource(dataSource);Map<Object, Object> dataSourceMap = DatasourceMapCache.getDataSourceMap();// 将默认数据源放入到目标数据源dataSourceMap.put("defaultDatasource", dataSource);// 设置目标数据源dynamicDatasource.setTargetDataSources(dataSourceMap);return dynamicDatasource;}@Beanpublic MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("dynamicDatasource") DynamicDatasource dynamicDatasource) throws Exception {MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dynamicDatasource);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));return sqlSessionFactoryBean;}@Beanpublic PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDatasource") DynamicDatasource dynamicDatasource) {return new DataSourceTransactionManager(dynamicDatasource);}
}
  • 数据源注解
/*** 数据源注解** @author Wxf* @since 2024-01-29 09:50:26**/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Ds {String value() default "datasource";
}
  • 注解切换
import com.dcits.metadata.config.datasource.DatasourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
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.stereotype.Component;import java.util.Objects;/*** 数据源 Aspect** @author Wxf* @since 2024-01-29 09:55:37**/
@Aspect
@Component
public class DynamicDatasourceAspect {// 切点@Pointcut("@annotation(com.dcits.metadata.config.datasource.aspect.Ds)")public void dynamicDatasource() {}// 环绕@Around("dynamicDatasource()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {try {// 类注解Class<?> clazz = joinPoint.getTarget().getClass();Ds clasDs = clazz.getAnnotation(Ds.class);// 方法注解MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Ds methodDs = methodSignature.getMethod().getAnnotation(Ds.class);if (Objects.nonNull(methodDs)) {// 方法上的数据源不为空则优先设置方法上的DatasourceContextHolder.setDataSource(methodDs.value());} else {DatasourceContextHolder.setDataSource(clasDs.value());}return joinPoint.proceed();} finally {DatasourceContextHolder.remove();}}
}
  • Spring上下文工具类

/*** Spring上下文工具类** @author Wxf* @since 2024-01-23 19:49:39**/
@Component
public class SpringApplicationContext implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringApplicationContext.applicationContext = applicationContext;}public static ApplicationContext getApplicationContext() {return applicationContext;}public static <T> T getBean(Class<T> clazz) {return applicationContext.getBean(clazz);}public static Object getBean(String name) {return applicationContext.getBean(name);}public static <T> T getBean(String name, Class<T> requiredType) {return applicationContext.getBean(name, requiredType);}// 推送Eventpublic static void publishEvent(Object event) {applicationContext.publishEvent(event);}
}
  • 动态数据源工具类

import com.dcits.metadata.config.datasource.DatasourceMapCache;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;import javax.sql.DataSource;/*** 动态数据源工具类** @author Wxf* @since 2024-03-05 16:11:38**/
public class HikariConfigUtils {/*** 初始化数据源** @param url      URL连接地址* @param driver   驱动* @param username 用户名* @param password 密码* @return 数据源*/public static DataSource initHikariDatasource(String url, String driver, String username, String password) {HikariConfig hikariConfig = new HikariConfig();hikariConfig.setJdbcUrl(url);hikariConfig.setDriverClassName(driver);hikariConfig.setUsername(username);hikariConfig.setPassword(password);return new HikariDataSource(hikariConfig);}/*** 刷新数据源** @param datasourceKey 数据源Key* @param dataSource    数据源*/public static void refreshDataSource(String datasourceKey, DataSource dataSource) {DatasourceMapCache.refreshDataSource(datasourceKey, dataSource);}
}

5. 测试

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author Wxf* @since 2024-01-27 15:03:50**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {@TableId(value = "id", type = IdType.AUTO)private Integer id;private String username;}
  • Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dcits.metadata.entity.User;
import org.apache.ibatis.annotations.Mapper;/*** @author Wxf* @since 2024-03-05 11:40:43**/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
  • UserService

import com.baomidou.mybatisplus.extension.service.IService;
import com.dcits.metadata.entity.User;import java.util.List;/*** 用户Service** @author Wxf* @since 2024-03-05 11:41:21**/
public interface UserService extends IService<User> {/*** 手动切换数据源** @return 用户列表*/List<User> selectUserList();/*** 手动切换数据源** @return 用户列表*/List<User> getDynamicUserList();
}
  • UserServiceImpl

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dcits.metadata.config.datasource.DatasourceContextHolder;
import com.dcits.metadata.entity.User;
import com.dcits.metadata.mapper.UserMapper;
import com.dcits.metadata.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.List;/*** 用户Service实现** @author Wxf* @since 2024-03-05 11:41:57**/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Overridepublic List<User> selectUserList() {return this.baseMapper.selectList(null);}@Overridepublic List<User> getDynamicUserList() {DatasourceContextHolder.setDataSource("ds3");List<User> userList = this.baseMapper.selectList(null);DatasourceContextHolder.remove();return userList;}
}
  • 测试数据源

import com.dcits.metadata.service.UserService;
import com.dcits.metadata.utils.HikariConfigUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import javax.sql.DataSource;/*** 测试动态数据源** @author Wxf* @since 2024-03-05 11:46:06**/
@SpringBootTest
public class UserTest {@Resourceprivate UserService userService;@Testvoid selectUserList() {System.out.println(this.userService.selectUserList());}@Testvoid getDynamicUserList() {DataSource dataSource = HikariConfigUtils.initHikariDatasource("jdbc:mysql://127.0.0.1:3306/ds3?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&useIPv6=false","com.mysql.cj.jdbc.Driver","root","root");HikariConfigUtils.refreshDataSource("ds3", dataSource);System.out.println(this.userService.getDynamicUserList());}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/724149.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用vscode前面几行被定住

当我们使用 vscode 滚动代码文档的时候&#xff0c;发现前面几行被定住了&#xff0c;想 css 的 sticky 一样&#xff0c;可能是之前我们不小心点到了这里&#xff0c;取消就好了

SoundTouch对音频处理(Android)

SoundTouch对音频处理&#xff08;Android&#xff09; SoundTouch介绍 SoundTouch 是一个用于音频处理的开源库&#xff0c;主要用于改变音频的速度、音调和音量等特征。您可以在项目中使用 SoundTouch 库来实现音频处理的功能&#xff0c;比如变速播放、音高变化、混响效果…

2024年阿里云服务器配置选择指南_个人和企业如何选择ECS实例规格?

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

亿发生产管控新篇章:mes系统专业推进,引领广州制造厂家数智转型

信息技术在企业发展中扮演着至关重要的角色。然而&#xff0c;随着生产组织方式向多品种、小批量转变&#xff0c;订单不断调整&#xff0c;制定计划需要根据市场和实际作业执行状态&#xff0c;不能仅仅依赖于物料和库存来控制生产。传统的生产现场管理已无法满足当今竞争激烈…

学习计算天数

学习计算天数 题目描述&#xff1a;解法思路&#xff1a;解法代码&#xff1a;运行结果&#xff1a; 题目描述&#xff1a; 输入y和m两个整数&#xff0c;y表示年份&#xff0c;m表示月份&#xff0c;计算y年m月有多少天&#xff0c;并输出天数。 测试1&#xff1a; 输⼊&…

121. 买卖股票的最佳时机【leetcode】/动态规划

121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从…

MySQL基础-----SQL语句之DCL数据控制语句

目录 前言 一、管理用户 1.查询用户 2.创建用户 3.修改用户密码 4.删除用户 案例 二、权限控制 1.查询权限 2.授予权限 3.撤销权限 案例 前言 本期我们学习SQL语句的最后一部分内容&#xff0c;也就是数据控制语句DCL。DCL英文全称是Data Control Language(数据控制语…

TypeScript 中,接口(Interface)

在 TypeScript 中&#xff0c;接口&#xff08;Interface&#xff09;是一种定义对象结构的类型。接口定义了对象应该具备的形状&#xff0c;即对象应该具有哪些属性和方法。 代码示例&#xff1a; // 定义一个接口&#xff0c;表示一个人的信息 interface Person { name:…

HarmonyOS NEXT应用开发案例集

概述 随着应用代码的复杂度提升&#xff0c;为了使应用有更好的可维护性和可扩展性&#xff0c;良好的应用架构设计变得尤为重要。本篇文章将介绍一个应用通用架构的设计思路&#xff0c;以减少模块间的耦合、提升团队开发效率&#xff0c;为开发者呈现一个清晰且结构化的开发…

深度学习模型九大经典初始化方案

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 1. 正态分布初始化 正态分布初始化将权重初始化为来自正态&#xff08;或高斯&#xff09;分布的随机数。该分布通常以0为均值&#xff0c;其标…

HTTPS是什么,那些行业适合部署呢?

随着在线活动的增加&#xff0c;对您共享的关键数据的威胁已经产生了严重的后果&#xff0c;包括欺诈性金融交易、在线身份盗窃等。此外&#xff0c;随着技术使用的增加&#xff0c;网络攻击也变得更加复杂和具有挑战性。 毫无疑问&#xff0c;互联网用户的数据安全意识成倍增长…

友思特应用 | 轻装上阵:便携式光谱仪,助力食品安全的便捷保障

欢迎访问官网了解更多&#xff1a;智能化手持光谱仪 | Linksqaure光谱仪 | 近红外便携式光谱仪 | 价液体检测台 | 广州友思特科技有限公司 导读 食品安全是一个备受瞩目的话题。在消费电子和光子学的进步推动下&#xff0c;手持式光学仪器可用于食品安全以及质量的评估&#x…

PaddleOCR CPU 文本文字识别 docker部署

需求&#xff1a; 需要把所有滑块图片的数据文字提取出来 启动服务 mkdir paddle cd paddle docker run -itd --name ppocr -v $PWD:/paddle --networkhost -it registry.baidubce.com/paddlepaddle/paddle:2.1.3-gpu-cuda10.2-cudnn7 /bin/bash docker exec -it ppocr bash …

蓝牙网关在智慧医疗的应用

蓝牙网关在智慧医疗的应用已经逐渐得到广泛关注。作为一种无线通信技术&#xff0c;蓝牙网关能够将各种蓝牙设备连接起来&#xff0c;实现数据传输和信息共享。在智慧医疗领域&#xff0c;蓝牙网关的应用不仅可以提高医疗服务的效率和质量&#xff0c;还可以为患者带来更好的医…

使用API接口竞品价格监控

步骤一&#xff1a;确定监控目标和KPIs 目标&#xff1a;明确您希望通过监控竞品价格来实现的目标&#xff0c;例如保持价格竞争力、检测价格波动等。KPIs&#xff1a;设定关键绩效指标&#xff0c;如价格变动幅度、价格调整频率等。 步骤二&#xff1a;选择数据源和API 电商…

2024年Android开发陷入饱和,,面试杀手锏

背景介绍 现在是一个信息大爆炸的时代&#xff0c;互联网世界中存在着无数的数据。信息就是这个时代最宝贵的资源&#xff0c;拥有了资源就相当于。互联网是块风水宝地&#xff0c;里面藏了无数的宝藏&#xff01;能不能挖到就看本事了。谁拥有了最全、最准确的信息&#xff0…

Jmeter 压测 —— 利用Docker分布式压测!

Jmeter分布式测试环境中有两个角色&#xff1a;Master 和 Slaves Master节点&#xff1a;向参与的Slaves节点发送测试脚本&#xff0c;并聚合Agent节点的执行结果。Slaves节点&#xff1a;接收并执行Master节点发送过来的测试脚本&#xff0c;并将执行结果返回给Master。 为什…

threejs展示glb模型

原模型为rvt模型 <template><div ref"threeJsContainer"class"three-js-container"></div> </template> <script> import { defineComponent } from "vue"; import * as THREE from "three"; import…

npm 私服以及使用

在工作中&#xff0c;公司有很多内部的包并不希望发布到npm官网仓库&#xff0c;因为可能涉及到一些私有代码不能暴露。对于前端来讲&#xff0c;这时就可以选择在公司内网搭建npm私有仓库。当前比较主流的几种解决方案&#xff1a;verdaccio、nexus、cnpm。大家可以按照自己的…

GEE遥感云大数据林业应用典型案例实践及GPT模型应用

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…