关于多数据源解决方案

目前在SpringBoot框架基础上多数据源的解决方案大多手动创建多个DataSource,后续方案有三:

  1. 继承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,使用AOP切面注入相应的数据源 ,但是这种做法仅仅适用单Service方法使用一个数据源可行,如果单Service方法有多个数据源执行会造成误读。
  2. 通过DataSource配置 JdbcTemplateBean,直接使用 JdbcTemplate操控数据源。
  3. 分别通过DataSource创建SqlSessionFactory并扫描相应的Mapper文件和Mapper接口。

MybatisPlus的多数据源

我通过阅读源码,发现MybatisPlus的多数据源解决方案正是AOP,继承了org.springframework.jdbc.datasource.AbstractDataSource,有自己对ThreadLocal的处理。通过注解切换数据源。也就是说,MybatisPlus只支持在单Service方法内操作一个数据源,毕竟官网都指明——“强烈建议只注解在service实现上”

而后,注意看com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder,也就是MybatisPlus是如何切换数据源的。

重点看:

  1. /**
  2. * 为什么要用链表存储(准确的是栈)
  3. * <pre>
  4. * 为了支持嵌套切换,如ABC三个service都是不同的数据源
  5. * 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。
  6. * 传统的只设置当前线程的方式不能满足此业务需求,必须模拟栈,后进先出。
  7. * </pre>
  8. */
  9. private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new ThreadLocal() {
  10. @Override
  11. protected Object initialValue() {
  12. return new ArrayDeque();
  13. }
  14. };

这段话翻译为大家都能懂得的意思就是“可以同时操控多个数据源”。那么,在MYSQL中,有语法为schemaName+. +tableName,如此一来就不会误走数据源了。

我继续看MybatisPlus是如何利用mybatis本身的ORM机制将实体类自动映射以及生成SQL语句的(这里插一句,MybatisPlus的源码易读懂,写的很不错)。无意看到了注解com.baomidou.mybatisplus.annotation.TableName中的schema,如果在类上加schema,在生成SQL语句时就会生成schemaName+. +tableName格式。

MybatisPlus多数据源事务(JTA

简单说明一下JTA

JTA包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager ) 两部分, 可以将资源管理器看做任意类型的持久化数据存储;事务管理器则承担着所有事务参与单元的协调与控制。

JTA只是提供了一个接口,并没有提供具体的实现。

不过Atomikos对其进行了实现,而后SpringBoot将其进行了整合,对其进行了托管,很方便开发者拿来即用。

其中事务管理器的主要部分为UserTransaction 接口,开发人员通过此接口在信息系统中实现分布式事务;而资源管理器则用来规范提供商(如数据库连接提供商)所提供的事务服务,它约定了事务的资源管理功能,使得 JTA 可以在异构事务资源之间执行协同沟通。

通常接入JTA步骤(目的就是让JTAUserTransaction接管驱动为分布式的数据源,通常为AtomikosDataSourceBean):

  1. 配置好AtomikosDataSourceBean
  2. AtomikosDataSourceBean交给SqlSessionFactory
  3. 配置UserTransaction事务管理。

但是我们用的是MybatisPlus,我们需要做的是接管MybatisPlus每一个数据源的配置,然后再把数据源依次交给MybatisPlus进行管理。

看看MybatisPlus是怎么进行多数据源配置的,源码里有这几个地方需要重点看一下:

  1. com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider,这个就是MybatisPlus多数据源配置的方式,利用HashMap来装载。
  2. com.baomidou.dynamic.datasource.DynamicDataSourceCreator,这个是每个数据源的配置方式。

其中com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider实现了接口com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider,是该接口的默认的实现。也就是说我们只需要实现该接口,自己配置多数据源以及每个数据源的驱动,成为该接口的默认实现就OK。

  • 实现该接口,配置多数据源:

    1. package xxx.xxx.xxx.config;
    2. import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
    3. import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
    4. import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
    5. import org.springframework.context.annotation.Primary;
    6. import org.springframework.stereotype.Service;
    7. import javax.sql.DataSource;
    8. import java.util.HashMap;
    9. import java.util.Map;
    10. /**
    11. * @author : zuoyu
    12. * @description : 接管MybatisPlus多数据源至Atomikos管理
    13. * @date : 2020-06-01 16:36
    14. **/
    15. @Service
    16. @Primary
    17. public class DynamicDataSourceProviderImpl implements DynamicDataSourceProvider {
    18. /**
    19. * 配置文件数据的松散绑定
    20. */
    21. private final DynamicDataSourceProperties properties;
    22. /**
    23. * Atomikos驱动数据源创建
    24. */
    25. private final AtomikosDataSourceCreator atomikosDataSourceCreator;
    26. public DynamicDataSourceProviderImpl(DynamicDataSourceProperties properties, AtomikosDataSourceCreator atomikosDataSourceCreator) {
    27. this.properties = properties;
    28. this.atomikosDataSourceCreator = atomikosDataSourceCreator;
    29. }
    30. @Override
    31. public Map<String, DataSource> loadDataSources() {
    32. Map<String, DataSourceProperty> dataSourcePropertiesMap = properties.getDatasource();
    33. Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size() * 2);
    34. for (Map.Entry<String, DataSourceProperty> item : dataSourcePropertiesMap.entrySet()) {
    35. String pollName = item.getKey();
    36. DataSourceProperty dataSourceProperty = item.getValue();
    37. dataSourceProperty.setPollName(pollName);
    38. dataSourceMap.put(pollName, atomikosDataSourceCreator.createDataSource(dataSourceProperty));
    39. }
    40. return dataSourceMap;
    41. }
    42. }
  • Atomikos驱动数据源创建:

    1. package xxx.xxx.xxx.config;
    2. import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
    3. import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
    4. import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
    5. import org.springframework.stereotype.Component;
    6. import javax.sql.DataSource;
    7. /**
    8. * @author : zuoyu
    9. * @description : 事务数据源
    10. * @date : 2020-06-01 17:30
    11. **/
    12. @Component
    13. public class AtomikosDataSourceCreator {
    14. /**
    15. * 创建数据源
    16. *
    17. * @param dataSourceProperty 数据源信息
    18. * @return 数据源
    19. */
    20. public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
    21. MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
    22. mysqlXaDataSource.setUrl(dataSourceProperty.getUrl());
    23. mysqlXaDataSource.setPassword(dataSourceProperty.getPassword());
    24. mysqlXaDataSource.setUser(dataSourceProperty.getUsername());
    25. AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
    26. xaDataSource.setXaDataSource(mysqlXaDataSource);
    27. xaDataSource.setMinPoolSize(5);
    28. xaDataSource.setBorrowConnectionTimeout(60);
    29. xaDataSource.setMaxPoolSize(20);
    30. xaDataSource.setXaDataSourceClassName(dataSourceProperty.getDriverClassName());
    31. xaDataSource.setTestQuery("SELECT 1 FROM DUAL");
    32. xaDataSource.setUniqueResourceName(dataSourceProperty.getPollName());
    33. return xaDataSource;
    34. }
    35. }
  • 配置JTA事务管理器:

    1. package xxx.xxx.xxx.config;
    2. import com.atomikos.icatch.jta.UserTransactionImp;
    3. import com.atomikos.icatch.jta.UserTransactionManager;
    4. import org.springframework.context.annotation.Bean;
    5. import org.springframework.context.annotation.Configuration;
    6. import org.springframework.context.annotation.DependsOn;
    7. import org.springframework.transaction.PlatformTransactionManager;
    8. import org.springframework.transaction.annotation.EnableTransactionManagement;
    9. import org.springframework.transaction.jta.JtaTransactionManager;
    10. import javax.transaction.TransactionManager;
    11. import javax.transaction.UserTransaction;
    12. /**
    13. * @author : zuoyu
    14. * @description : 分布式事务配置
    15. * @date : 2020-06-01 17:55
    16. **/
    17. @Configuration
    18. @EnableTransactionManagement
    19. public class TransactionManagerConfig {
    20. @Bean(name = "userTransaction")
    21. public UserTransaction userTransaction() throws Throwable {
    22. UserTransactionImp userTransactionImp = new UserTransactionImp();
    23. userTransactionImp.setTransactionTimeout(10000);
    24. return userTransactionImp;
    25. }
    26. @Bean(name = "atomikosTransactionManager")
    27. public TransactionManager atomikosTransactionManager() throws Throwable {
    28. UserTransactionManager userTransactionManager = new UserTransactionManager();
    29. userTransactionManager.setForceShutdown(false);
    30. return userTransactionManager;
    31. }
    32. @Bean(name = "transactionManager")
    33. @DependsOn({"userTransaction", "atomikosTransactionManager"})
    34. public PlatformTransactionManager transactionManager() throws Throwable {
    35. return new JtaTransactionManager(userTransaction(), atomikosTransactionManager());
    36. }
    37. }

如此,即可

这样一来便可解决MybatisPlus多数据源的误走,且支持多数据源下的事务问题。

做任何事情,重要的是思路,而不是搬砖。

本文首发于我的个人博客左羽(一杯茶)

MybatisPlus多数据源及事务解决思路的更多相关文章

  1. 【spring boot】SpringBoot初学(7)– 多数据源及其事务

    前言 github: https://github.com/vergilyn/SpringBootDemo 代码位置: 参考: Spring Boot Reference Guide , §77.2 ...

  2. MySQL在并发场景下的问题及解决思路

    目录 1.背景 2.表锁导致的慢查询的问题 3.线上修改表结构有哪些风险? 4.一个死锁问题的分析 5.锁等待问题的分析 6.小结 1.背景 对于数据库系统来说在多用户并发条件下提高并发性的同时又要保 ...

  3. C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路

    C#不用union,而是有更好的方式实现   用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...

  4. chosen.jquery.js 搜索框只能从头匹配的解决思路+方法

    chosen.jquery.js 搜索框只能从头匹配的解决思路+方法 心急者请直接看下方 总结 ,由于本问题未能找到直接答案,所以只能通过修改源码解决.故将修改源码思路贴出来供大家参考,在遇到其他改源 ...

  5. springboot+atomikos+多数据源管理事务(mysql 8.0)

    jta:Java Transaction API,即是java中对事务处理的api 即 api即是接口的意思 atomikos:Atomikos TransactionsEssentials 是一个为 ...

  6. Spring 下,关于动态数据源的事务问题的探讨

    开心一刻 毒蛇和蟒蛇在讨论谁的捕猎方式最高效. 毒蛇:我只需要咬对方一口,一段时间内它就会逐渐丧失行动能力,最后死亡. 蟒蛇冷笑:那还得等生效时间,我只需要缠住对方,就能立刻致它于死地. 毒蛇大怒:你 ...

  7. C# 高并发、抢单解决思路

    高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求.高并发相关常用的一些指标有响应时间(Respon ...

  8. 关于Spring Boot 多数据源的事务管理

    自己的一些理解:自从用了Spring Boot 以来,这近乎零配置和"约定大于配置"的设计范式用着确实爽,其实对零配置的理解是:应该说可以是零配置可以跑一个简单的项目,因为Spri ...

  9. Jquery UI - DatePicker 在Dialog中无法自动隐藏的解决思路

    通过Jquery UI Dialog模态展示如下的一个员工编辑页面,但是遇到一个奇怪的问题:点击Start Date的input元素后,其无法失去焦点.从而导致DatePicker控件在选择日期后无法 ...

随机推荐

  1. sessionStorage 退出登录删除sessionStorage储存

    1 登录后在sessionStorage中添加token的值,退出后清空 登录后在sessionStorage中添加token的值,退出后清空 localStorage 和 sessionStorag ...

  2. 处理fMRI数据的一些常用Matlab命令

    背景 处理fMRI数据常常用到MATLAB,在此记录一些常用代码及功能. 1.读取原始DICOM数据 1-1 读入dicom图像并绘图: Image = dicomread('fMRI.dcm'); ...

  3. 华为Mate20 Adb驱动失败

    今天拿到同事一台华为Mate20,准备装个包,结果发现adb一直 no devices,AndroidStudio当然也显示 no connected devices 开发者模式也打开了,USB调试也 ...

  4. 最速下降法--MATLAB程序

    function x = fxsteep(f,e,a,b)x1 = a;x2 = b;Q = fxhesson(f,x1,x2);x0 = [x1,x2]';temp = [x0];fx1 = dif ...

  5. RESTful API风格

    前言 之前写的接口,有用过Webservices,MVC,ashx,但都没个统一的请求规范,随百度. 参考链接,原文出处 http://www.ruanyifeng.com/blog/2014/05/ ...

  6. 实验2 C语言表达式编程应用及输入输出函数( 后附炫彩小人:) )

    实验任务一 #include <stdio.h> int main (){ int a=5,b=7,c=100,d,e,f; d=a/b*c; e=a*c/b; f=c/b*a; prin ...

  7. 为什么会有kafka消息系统?小问题藏着大细节!

    前言:老刘今天写这篇文章首先想对一些复制粘贴的博客表达不满:其次是想用通俗易懂的话解释消息系统:最后欢迎各位英雄好汉.女中豪杰前来battle. 1. 为什么有消息系统? 1.1 背景 今天复习kaf ...

  8. 在.NET Core 中收集数据的几种方式

    APM是一种应用性能监控工具,可以帮助理解系统行为, 用于分析性能问题的工具,以便发生故障的时候,能够快速定位和解决问题, 通过汇聚业务系统各处理环节的实时数据,分析业务系统各事务处理的交易路径和处理 ...

  9. 在jsp页面嵌入java代码让某些div显示或者隐藏

    <!--监测评价人显示评价人信息 --> <% if("D3".equals(role_flag)){%> <div id="crud&qu ...

  10. FPT: Feature Pyramid Transfomer

    导言: 本文介绍了一个在空间和尺度上全活跃特征交互(fully active feature interaction across both space and scales)的特征金字塔transf ...