多数据源配置(MyBatis-Plus vs AbstractRoutingDataSource)

MyBatis-Plus vs AbstractRoutingDataSource

MyBatis-Plus多数据源配

1.添加依赖

<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version>
</dependency>

2.配置数据源信息

spring:datasource:dynamic:primary: master  # 设置默认数据源datasource:master:  # 主数据源url: jdbc:mysql://localhost:3306/master_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave:  # 从数据源url: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver

3.使用 @DS 注解切换数据源
在Service类或方法上使用 @DS(“数据源名称”) 指定数据源

@Service
@DS("master")  // 类级别默认数据源
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Override@DS("slave")  // 方法级别覆盖类级别数据源public User getUserById(Long id) {return userMapper.selectById(id);}
}

4.排除原生自动配置(可选)
如果出现冲突,在启动类排除原生数据源自动配置:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

5.多数据源的事务一致性(@DSTransactional注解)

@DSTransactionalpublic void test() {//db1数据源DeAnOrganization deAnOrganization = new DeAnOrganization();deAnOrganization.setId(1L);deAnOrganizationMapper.insert(deAnOrganization);//db2数据源	JdfLightBasic jdfLightBasic = new JdfLightBasic();jdfLightBasic.setOrgId("1");jdfLightBasicMapper.insert(jdfLightBasic);//抛出异常,@DSTransactional可保证事务唯一性,事务可正常回滚throw new RuntimeException("test");}

AbstractRoutingDataSource

使用 AbstractRoutingDataSource 配置多数据源是 Spring 原生的动态数据源解决方案,适合需要深度定制的场景。
1.依赖仅需引入jdbc+连接池

<!-- 仅需 Spring JDBC + 连接池(使用druid) -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>

2.数据源配置

spring:datasource:master:url: jdbc:mysql://localhost:3306/master_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5max-active: 20slave:url: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 3max-active: 15

3.创建数据源上下文持有类

public class DataSourceContextHolder {private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();public static void setDataSource(String dsName) {CONTEXT.set(dsName);}public static String getDataSource() {return CONTEXT.get();}public static void clear() {CONTEXT.remove();}
}

4.实现动态数据源路由

public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}
}

5.配置数据源Bean

@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class DataSourceConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.master")public DataSource masterDataSource() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.slave")public DataSource slaveDataSource() {return DruidDataSourceBuilder.create().build();}@Bean@Primarypublic DataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource master,@Qualifier("slaveDataSource") DataSource slave) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("master", master);targetDataSources.put("slave", slave);DynamicDataSource ds = new DynamicDataSource();ds.setDefaultTargetDataSource(master); // 默认数据源ds.setTargetDataSources(targetDataSources);ds.afterPropertiesSet();return ds;}// 配置事务管理器@Beanpublic PlatformTransactionManager transactionManager(DataSource dynamicDataSource) {return new DataSourceTransactionManager(dynamicDataSource);}
}

6.自定义注解实现切换

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {String value() default "master";
}// 切面实现
@Aspect
@Component
public class DSAspect {@Before("@annotation(ds)")public void beforeSwitchDS(JoinPoint point, DS ds) {DataSourceContextHolder.setDataSource(ds.value());}@After("@annotation(ds)")public void afterSwitchDS(JoinPoint point, DS ds) {DataSourceContextHolder.clear();}
}

7.使用示例

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@DS("master") // 使用主库public void addUser(User user) {userMapper.insert(user);}@DS("slave") // 使用从库public User getUser(Long id) {return userMapper.selectById(id);}
}

8.事务管理器配置

@Configuration
public class TransactionConfig {// 主库事务管理器@Beanpublic PlatformTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}// 从库事务管理器@Beanpublic PlatformTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}
// 自定义数据源切换注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {String value() default "master";
}// 数据源切换切面(需优先于事务切面执行)
@Aspect
@Component
@Order(0) // 优先级高于 @Transactional 的切面
public class DSAspect {@Before("@annotation(ds)")public void beforeSwitchDS(JoinPoint joinPoint, DS ds) {DataSourceContextHolder.setDataSource(ds.value());}@After("@annotation(ds)")public void afterSwitchDS(JoinPoint joinPoint, DS ds) {DataSourceContextHolder.clear();}
}
@Service
public class BusinessService {// 显式指定事务管理器@DS("master")@Transactional(transactionManager = "masterTransactionManager")public void createOrder(Order order) {// 主库操作}@DS("slave")@Transactional(transactionManager = "slaveTransactionManager")public Order queryOrder(Long id) {// 从库查询}// 跨数据源事务(需要分布式事务支持)@DS("master")@Transactional(transactionManager = "masterTransactionManager")public void crossDataSourceOperation() {// 主库操作slaveOperation(); // 需要新事务}@DS("slave")@Transactional(transactionManager = "slaveTransactionManager", propagation = Propagation.REQUIRES_NEW)public void slaveOperation() {// 从库操作(独立事务)}
}

关键注意事项

AOP 执行顺序
必须确保数据源切换切面(@DS)先于事务切面执行:
// 启动类配置
@SpringBootApplication
@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE) // 事务切面最后执行
public class Application { ... }事务传播机制
默认 Propagation.REQUIRED 会继承当前事务的数据源
使用 Propagation.REQUIRES_NEW 可强制开启新事务并切换数据源分布式事务支持
如需跨数据源事务原子性,需集成 Seata 等分布式事务框架:
@DS("master")
@GlobalTransactional // Seata 全局事务注解
public void distributedTransaction() {// 操作多个数据源
}

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

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

相关文章

聊透多线程编程-线程互斥与同步-13. C# Mutex类实现线程互斥

目录 一、什么是临界区&#xff1f; 二、Mutex类简介 三、Mutex的基本用法 解释&#xff1a; 四、Mutex的工作原理 五、使用示例1-保护共享资源 解释&#xff1a; 六、使用示例2-跨进程同步 示例场景 1. 进程A - 主进程 2. 进程B - 第二个进程 输出结果 ProcessA …

stm32week12

stm32学习 九.stm32与HAL库 2.HAL库框架 总架构&#xff1a; 文件介绍&#xff1a; ppp是某一外设&#xff0c;ex是拓展功能 HAL库API函数和变量命名规则&#xff1a; HAL库对寄存器位操作的相关宏定义&#xff1a; HAL库的回调函数&#xff1a; 3.STM32启动过程 MDK编译过…

opencv HSV的具体描述

色调H&#xff1a; 使用角度度量&#xff0c;取值范围为0\~360&#xff0c;从红色开始按逆时针方向计算&#xff0c;红色为0&#xff0c;绿色为120&#xff0c;蓝色为240。它们的补色是&#xff1a;黄色为60&#xff0c;青色为180&#xff0c;紫色为300。通过改变H的值&#x…

Java Lambda表达式指南

一、Lambda表达式基础 1. 什么是Lambda表达式&#xff1f; 匿名函数&#xff1a;没有名称的函数函数式编程&#xff1a;可作为参数传递的代码块简洁语法&#xff1a;替代匿名内部类的更紧凑写法 2. 基本语法 (parameters) -> expression 或 (parameters) -> { statem…

面向对象设计中的类的分类:实体类、控制类和边界类

目录 前言1. 实体类&#xff08;Entity Class&#xff09;1.1 定义和作用1.2 实体类的特点1.3 实体类的示例 2. 控制类&#xff08;Control Class&#xff09;2.1 定义和作用2.2 控制类的特点2.3 控制类的示例 3. 边界类&#xff08;Boundary Class&#xff09;3.1 定义和作用3…

C# 封装教程

原文&#xff1a;C# 封装_w3cschool &#xff08;注&#xff1a;本文为教程文章&#xff0c;请勿标记为付费文章&#xff01;特此声明&#xff09; 封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中&#xff0c;封装是…

量化交易 - RSRS(阻力支撑相对强度)- 正确用法 - 年均收益18%

经过研究&#xff0c;发现RSRS的正确用法其实是需要用到两个数据&#xff0c;分别是 n: 一阶拟合样本数&#xff0c;m:求均值方差样本数&#xff0c;其中n比较小 如18&#xff0c;m比较大 如1100 经过调优后&#xff0c;收益率显著上升&#xff01; 如下图&#xff1a; &…

Oracle expdp的 EXCLUDE 参数详解

Oracle expdp的 EXCLUDE 参数详解 EXCLUDE 是 Oracle Data Pump Export (expdp) 工具中的一个关键参数&#xff0c;用于指定在导出过程中要排除的对象或对象类型。 一、基本语法 expdp username/password DUMPFILEexport.dmp DIRECTORYdpump_dir EXCLUDEobject_type[:name_c…

如何使用3DMAX插件PFSpliner将3D对象转化为艺术样条线?

什么是粒子流源(Particle Flow)是3DMAX的一个功能极其强大的粒子系统。它采用事件驱动模型,使用一个名为“粒子视图”的特殊对话框。在“粒子视图”中,您可以将描述粒子属性(如形状、速度、方向和一段时间内的旋转)的单个运算符组合成称为事件的组。每个操作符都提供一组…

【python】 循环语句(while)

1、循环语句 语法&#xff1a; while 条件:......... #只有条件为真时&#xff0c;才会执行while中的内容。 1.1循环语句基本使用 示例1&#xff1a; print("开始") while 1>2:print("人生得意须尽欢") print("结束") #输出结果&#…

OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比

OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比 目录 OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于OOA-CN…

20250421在荣品的PRO-RK3566开发板的Android13下频繁重启RKNPU fde40000.npu: Adding to iommu gr

20250421在荣品的PRO-RK3566开发板的Android13下频繁重启RKNPU fde40000.npu: Adding to iommu gr 2025/4/21 14:50 缘起&#xff1a;电池没电了&#xff0c;导致荣品的PRO-RK3566的核心板频繁重启。 内核时间4s就重启。100%复现。 PRO-RK3566 Android13启动到这里 复位&#…

动态监控进程

1.介绍: top和ps命令很相似,它们都是用来显示正在执行的进程,top和ps最大的不同之处,在于top在执行中可以更新正在执行的进程. 2.基本语法&#xff1a; top [选项] 选项说明 ⭐️僵死进程&#xff1a;内存没有释放,但是进程已经停止工作了,需要及时清理 交互操作说明 应用案…

657SJBH西藏藏药特产销售管理系统

毕业论文&#xff08;设计&#xff09;文献综述 西藏藏药特产销售管理系统的设计与实现 近年来&#xff0c;随着网络技术特别是Internet技术的普及和发展&#xff0c;电子商务的开发和应用成为一个热门领域&#xff0c;在线藏药特产销售系统就是这其中的一员。 藏药产业在西藏…

栈和队列--数据结构初阶(2)(C/C++)

文章目录 前言理论部分栈的模拟实现STL中的栈容器队列的模拟实现STL中的队列容器 作业部分 前言 这期的话会给大家讲解栈和队列的模拟实现和在STL中栈和队列怎么用的一些知识和习题部分(这部分侧重于理论知识&#xff0c;习题倒还是不难) 理论部分 栈的模拟实现 typedef int…

RNN的理解

对于RNN的理解 import torch import torch.nn as nn import torch.nn.functional as F# 手动实现一个简单的RNN class RNN(nn.Module):def __init__(self, input_size, hidden_size, output_size):super(RNN, self).__init__()# 定义权重矩阵和偏置项self.hidden_size hidden…

二叉查找树和B树

二叉查找树&#xff08;Binary Search Tree, BST&#xff09;和 B 树&#xff08;B-tree&#xff09;都是用于组织和管理数据的数据结构&#xff0c;但它们在结构、应用场景和性能方面有显著区别。 二叉查找树&#xff08;Binary Search Tree, BST&#xff09; 特点&#xff1…

一段式端到端自动驾驶:VAD:Vectorized Scene Representation for Efficient Autonomous Driving

论文地址&#xff1a;https://github.com/hustvl/VAD 代码地址&#xff1a;https://arxiv.org/pdf/2303.12077 1. 摘要 自动驾驶需要对周围环境进行全面理解&#xff0c;以实现可靠的轨迹规划。以往的方法依赖于密集的栅格化场景表示&#xff08;如&#xff1a;占据图、语义…

OpenCV训练题

一、创建一个 PyQt 应用程序&#xff0c;该应用程序能够&#xff1a; 使用 OpenCV 加载一张图像。在 PyQt 的窗口中显示这张图像。提供四个按钮&#xff08;QPushButton&#xff09;&#xff1a; 一个用于将图像转换为灰度图一个用于将图像恢复为原始彩色图一个用于将图像进行…

opencv函数展示4

一、形态学操作函数 1.基本形态学操作 &#xff08;1&#xff09;cv2.getStructuringElement() &#xff08;2&#xff09;cv2.erode() &#xff08;3&#xff09;cv2.dilate() 2.高级形态学操作 &#xff08;1&#xff09;cv2.morphologyEx() 二、直方图处理函数 1.直方图…