因为项目中用到了多数据源 不可避免的会有各种各样的问题
列一下最主要的问题:

1 如何配置多数据源???
2 spring项目中多数据源无法切换???
3 操作了2个或者2个以上数据库的数据无法保证事务的一致性(也就是多数据源事务管理)

上面的3个问题 其实我只遇到了其中的一个 (spring项目中多数据源无法切换)就是加了@Transactional注解后 数据源无法切换的问题 这个问题当时就解决了 知其然还要知其所以然, 所以现在没有那么忙了 就把问题终结一下 知道其中的原理

1 项目的整合
springboot + mybatis puls + dynamic
boot的版本为 2.2.6
pom :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--mybatis puls-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3</version>
</dependency>
<!--多数据源配置-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!--druid 连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--oracle驱动-->
<dependency>
<groupId>com.ymtc.platform</groupId>
<artifactId>ojdbc6</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--去除内嵌tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

<!--websocket依赖包-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>8.5.23</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.0.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>

yml:
server:
port: 8088
# rocketmq 4.9.3
#rocketmq:
# # 多个用;隔开
# name-server: 10.22.15.61:9876;10.22.15.60:9876
mybatis-plus:
global-config:
#刷新mapper 调试神器
refresh-mapper: true
#实体扫描,多个package用逗号或者分号分隔
#typeAliasesPackage: com.bs.platform.bizform.entity.*
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 3
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
configuration:
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
map-underscore-to-camel-case: true
cache-enabled: false
#配置JdbcTypeForNull, oracle数据库必须配置
jdbc-type-for-null: 'null'
spring:
main:
#重名会覆盖 默认会报错无法启动
allow-bean-definition-overriding: true
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
type: com.alibaba.druid.pool.DruidDataSource
dynamic:
druid: #以下是全局默认值,可以全局更改
#监控统计拦截的filters
filters: stat,wall
#配置初始化大小/最小/最大
initial-size: 1
min-idle: 1
max-active: 20
datasource:
master:
url: jdbc:oracle:thin:@10.22.32.17:1521/midtdb
username: esi_comm
password: esi_test_2022
driver-class-name: oracle.jdbc.driver.OracleDriver
iadquery:
url: jdbc:oracle:thin:@10.22.13.71:1521/yaetdb
username: iadsmpm_query
password: iadsmpm_query_test2022
driver-class-name: oracle.jdbc.driver.OracleDriver
(一些更加细致的配置和作用这里就不细说了)
到此 整合工作就可以完成了 就能够启动项目 访问数据库了第一个问题就解决了
补充一下:
dynamic 多数据源的配置 使用 @DS 注解来切换数据源 value就是yml z中配置的数据源的名称 这个@DS注解可以放到类或者方法上, baomido的官网建议的是这个注解应该用到service层的实现类上或者方法上,具体的影响可以上官网看看

2 数据源切换的问题

@Service
public class aaaServiceImpl implements aaaService {

@Autowired // 访问master数据库
private SmartPmTblCurrentMapper smartPmTblCurrentDao;
@Autowired // 访问iadquery数据库
private SmartPMAutoCallService service;
@Override
public String asd() {
List<SmartPmTblCurrent> smartPmTblCurrents = smartPmTblCurrentDao.selectList(null);
List<SmartPMChartMoveTarget> chartData = service.getChartData();
return "12123123";
}
}

这个时候没有如何问题

在aaaServiceImpl 类上加上 @Transactional 开启事务

java.sql.SQLSyntaxErrorException: ORA-00942: 表或视图不存在
通过排查 发现数据源没有切换过来导致当前的库中找不到查询的表
问题是为什么数据源这个时候就无法切换呢?

这个方法的确定目标数据源方法(determineTargetDataSource)代码为:

/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
看到这里的determineCurrentLookupKey方法的时候一切都能明了了。

那么再回到本文的题目,spring项目中多数据源无法切换,为什么呢?
因为我在代码中对于动态数据源的切换代码写在了service方法内了,而那个service的方法头上又加上了@Transactional注解,那么在spring框架执行的过程中,它先执行TransactionInterceptor的方法,而后再去执行我所写的service的具体代码,而在TransactionInterceptor执行的过程中它会先获取到数据源的连接,正因为如此,那么切换无论我在service里怎么写切换的数据源的代码它都不会再去执行了。
说的直白一点 总结一下: 就是加上事务的时候 开启事务会缓存 DataSource和SqlSessionTemplate信息 导致数据源没有办法切换
怎么去解决:
手动的去切数据源(这里就不展开说)
需要切换数据源的service代码不要开启事务
将切换数据源的代码写在service层之前,即写在controller层里

3 重点来了 前面的解决了问题 那么当我们同时要修改2个数据库 这个时候肯定是需要事务的支持的 那怎么办???

JTA基本概念
JTA即Java-Transaction-API,JTA允许应用程序执行分布式事务处理,即在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序对JTA的支持极大地增强了数据访问能力。

XA协议是数据库层面的一套分布式事务管理的规范,JTA是XA协议在Java中的实现,多个数据库或是消息厂商实现JTA接口,开发人员只需要调用SpringJTA接口即可实现JTA事务管理功能。

JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中

分布式事务
分布式事务(DistributedTransaction)包括事务管理器(TransactionManager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager )。

资源管理器是任意类型的持久化数据存储容器,例如在开发中常用的关系型数据库:MySQL,Oracle等,消息中间件RocketMQ、RabbitMQ等。

事务管理器提供事务声明,事务资源管理,同步,事务上下文传播等功能,并且负责着所有事务参与单元者的相互通讯的责任。JTA规范定义了事务管理器与其他事务参与者交互的接口,其他的事务参与者与事务管理器进行交互。
pom加上
<!--JTA组件核心依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

yml加上
spring:
jta:
transaction-manager-id: jtaManager

我们就以2个数据源为例
配置类:
DruidIadqueryConfig
@Configuration
@MapperScan(basePackages = {"com.bs.dao.iadquery"},sqlSessionTemplateRef = "iadquerySqlSessionTemplate")
public class DruidIadqueryConfig {

@Autowired
private IadqueryConfig iadqueryConfig;

@Bean("dataSourceTwo")
public DataSource dataSourceOne () throws SQLException {
// 设置数据库连接
// MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
OracleXADataSource oracleXADataSource = new OracleXADataSource();
oracleXADataSource.setURL(iadqueryConfig.getUrl());
oracleXADataSource.setUser(iadqueryConfig.getUsername());
oracleXADataSource.setPassword(iadqueryConfig.getPassword());
oracleXADataSource.setDriverType(iadqueryConfig.getDriverClassName());
// 事务管理器
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(oracleXADataSource);
atomikosDataSourceBean.setUniqueResourceName("dataSourceTwo");
return atomikosDataSourceBean;
}

@Bean(name = "sqlSessionFactoryTwo")
public SqlSessionFactory sqlSessionFactoryOne(
@Qualifier("dataSourceTwo") DataSource dataSourceOne) throws Exception{
// 配置Session工厂
// SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSourceOne);
return sessionFactory.getObject();
}
@Bean(name = "iadquerySqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(
@Qualifier("sqlSessionFactoryTwo") SqlSessionFactory sqlSessionFactory) {
// 配置Session模板
return new SqlSessionTemplate(sqlSessionFactory);
}
}

@Configuration
@MapperScan(basePackages = {"com.bs.dao.master"},sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class DruidMasterConfig {

@Autowired
private MasterConfig masterConfig ;

@Primary //
@Bean("dataSourceOne")
public DataSource dataSourceOne () throws SQLException {
// 设置数据库连接
// MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
OracleXADataSource oracleXADataSource = new OracleXADataSource();
oracleXADataSource.setURL(masterConfig.getUrl());
oracleXADataSource.setUser(masterConfig.getUsername());
oracleXADataSource.setPassword(masterConfig.getPassword());
oracleXADataSource.setDriverType(masterConfig.getDriverClassName());
// 事务管理器
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(oracleXADataSource);
atomikosDataSourceBean.setUniqueResourceName("dataSourceOne");
return atomikosDataSourceBean;
}
@Primary
@Bean(name = "sqlSessionFactoryOne")
public SqlSessionFactory sqlSessionFactoryOne(
@Qualifier("dataSourceOne") DataSource dataSourceOne) throws Exception{
// 配置Session工厂
// SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSourceOne);
return sessionFactory.getObject();
}
@Primary
@Bean(name = "masterSqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(
@Qualifier("sqlSessionFactoryOne") SqlSessionFactory sqlSessionFactory) {
// 配置Session模板
return new SqlSessionTemplate(sqlSessionFactory);
}
}

@Configuration
@Data
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.iadquery")
public class IadqueryConfig {
private String url;
private String username;
private String password;
private String driverClassName;
}

@Configuration
@Data
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public class MasterConfig {
private String url;
private String username;
private String password;
private String driverClassName;
}

加上事务启动项目:
2022-10-09 17:35:28 DEBUG org.springframework.web.servlet.DispatcherServlet:1131 - Completed 200 OK

打完 欢迎交流

spring boot 整合mybatis 配置多数据源 数据源切换和多数据源的事务的更多相关文章

  1. Spring Boot整合Mybatis配置详解

    首先,你得有个Spring Boot项目. 平时开发常用的repository包在mybatis里被替换成了mapper. 配置: 1.引入依赖: <dependency> <gro ...

  2. 太妙了!Spring boot 整合 Mybatis Druid,还能配置监控?

    Spring boot 整合 Mybatis Druid并配置监控 添加依赖 <!--druid--> <dependency> <groupId>com.alib ...

  3. Spring Boot整合Mybatis并完成CRUD操作

    MyBatis 是一款优秀的持久层框架,被各大互联网公司使用,本文使用Spring Boot整合Mybatis,并完成CRUD操作. 为什么要使用Mybatis?我们需要掌握Mybatis吗? 说的官 ...

  4. Spring Boot系列(三):Spring Boot整合Mybatis源码解析

    一.Mybatis回顾 1.MyBatis介绍 Mybatis是一个半ORM框架,它使用简单的 XML 或注解用于配置和原始映射,将接口和Java的POJOs(普通的Java 对象)映射成数据库中的记 ...

  5. spring boot 整合 mybatis 以及原理

    同上一篇文章一样,spring boot 整合 mybatis过程中没有看见SqlSessionFactory,sqlsession(sqlsessionTemplate),就连在spring框架整合 ...

  6. Spring Boot 整合mybatis时遇到的mapper接口不能注入的问题

    现实情况是这样的,因为在练习spring boot整合mybatis,所以自己新建了个项目做测试,可是在idea里面mapper接口注入报错,后来百度查询了下,把idea的注入等级设置为了warnin ...

  7. Spring Boot整合Mybatis报错InstantiationException: tk.mybatis.mapper.provider.base.BaseSelectProvider

    Spring Boot整合Mybatis时一直报错 后来发现原来主配置类上的MapperScan导错了包 由于我使用了通用Mapper,所以应该导入通用mapper这个包

  8. Spring Boot整合MyBatis(非注解版)

    Spring Boot整合MyBatis(非注解版),开发时采用的时IDEA,JDK1.8 直接上图: 文件夹不存在,创建一个新的路径文件夹 创建完成目录结构如下: 本人第一步习惯先把需要的包结构创建 ...

  9. Spring Boot整合Mybatis完成级联一对多CRUD操作

    在关系型数据库中,随处可见表之间的连接,对级联的表进行增删改查也是程序员必备的基础技能.关于Spring Boot整合Mybatis在之前已经详细写过,不熟悉的可以回顾Spring Boot整合Myb ...

  10. Spring Boot 整合 Mybatis 实现 Druid 多数据源详解

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “清醒时做事,糊涂时跑步,大怒时睡觉,独处时思考” 本文提纲一.多数据源的应用场景二.运行 sp ...

随机推荐

  1. MogDB备机处于standby need-repair(WAL)状态

    MogDB 备机处于 standby need-repair(WAL)状态 本文出处:https://www.modb.pro/db/402820 问题现象 Mogdb 主备环境,备机检查发现 Sta ...

  2. 重新整理 .net core 实践篇———承载[外篇]

    前言 简单介绍一下承载. 正文 名称叫做承载,其实就是.net core 定义的一套长期运行的服务的规范. 这个服务可以是web服务,也可以是其他服务,比如tcp,或者一些监控服务. 这里以监控服务为 ...

  3. nginx重新整理——————nginx 模块[十]

    前言 简单介绍一下nginx的模块. 正文 https://nginx.org/en/docs/ 这里面可以看到官方模块. 比如打开这个模块: https://nginx.org/en/docs/ht ...

  4. c#采用toml做配置文件的坑过

    这几天在玩个程序,突然看到c#采用图toml文件,好用,直观,确实也简单. 不过...... github上示例写的 TOML to TomlTable TOML input file:v Enabl ...

  5. 【Oracle】lpad与rpad函数,从左边对字符串使用指定的字符进行填充将1变成000001

    [Oracle]lpad&rpad函数,从左边对字符串使用指定的字符进行填充将1变成000001 lpad与rpad函数 顾名思义,lpad是从左边开始进行填充,rpad是从右边开始进行填充 ...

  6. 剑指offer66(Java)-构建乘积数组(中等)

    题目: 给定一个数组 A[0,1,-,n-1],请构建一个数组 B[0,1,-,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×-×A[i ...

  7. HarmonyOS NEXT应用开发之深色跑马灯案例

    介绍 本示例介绍了文本宽度过宽时,如何实现文本首尾相接循环滚动并显示在可视区,以及每循环滚动一次之后会停滞一段时间后再滚动. 效果图预览 使用说明: 1.进入页面,检票口文本处,实现文本首尾相接循环滚 ...

  8. Spark如何对源端数据做切分?

    简介: 典型的Spark作业读取位于OSS的Parquet外表时,源端的并发度(task/partition)如何确定?特别是在做TPCH测试时有一些疑问,如源端扫描文件的并发度是如何确定的?是否一个 ...

  9. 汽车之家基于 Flink 的数据传输平台的设计与实践

    简介: 数据接入与传输作为打通数据系统与业务系统的一道桥梁,是数据系统与架构中不可或缺的一个重要部分.数据传输系统稳定性和准确性,直接影响整个数据系统服务的 SLA 和质量.此外如何提升系统的易用性, ...

  10. 当新零售遇上 Serverless

    ​简介: Serverless 的出现给传统企业数字化转型带了更多机遇. 某零售商超行业的龙头企业,其主要业务涵盖购物中心.大卖场.综合超市.标准超市.精品超市.便利店及无人值守智慧商店等零售业态,涉 ...