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

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. Python 注释:解释和优化代码可读性

    注释可以用来解释Python代码.注释可以用来使代码更易读.注释可以用来在测试代码时防止执行. 创建注释 注释以#开始,Python会忽略它们: 示例:获取您自己的Python注释 # 这是一个注释 ...

  2. 【FAQ】HarmonyOS SDK 闭源开放能力 —Scan Kit

    1.问题描述 Scan Kit扫描专用底层码流接口需要鉴权,鉴权失败后功能还能用吗? 解决方案 如果已经申请过白名单,因为异常导致的鉴权失败会优先放通,保障业务成功. 2.问题描述 调用Scan Ki ...

  3. CentOS9 \ Centos8安装MySQL 8步骤

    centos8 rpm 安装mysql8.0.28_太阳神LoveU的博客-CSDN博客 This upper link is still working for mysql 8 on the Cen ...

  4. redis 简单整理——客户端案例分析[十八]

    前言 简单整理一下客户端案例分析. 正文 现象一: 服务端现象:Redis主节点内存陡增,几乎用满maxmemory,而从节点 内存并没有变化. 客户端现象:客户端产生了OOM异常,也就是Redis主 ...

  5. js es6 delete

    前言 首先delete 不同于nodejs delete,看下有什么不同. 正文 var test=5; delete test; console.log(test); 结果是test没有受到任何影响 ...

  6. websocket fleck demo

    前言 fleck 比较简洁,想看下他的源码的,先感受一下demo吧. 正文 先上代码. static IDictionary<string, IWebSocketConnection> d ...

  7. C语言专业课复试整理

    C复试专业基础测试整理 运行C程序的步骤和方法 编辑.编译.连接和运行 . 编辑是用户把编写好的C语言源程序输入计算机,以文本文件的形式存放在磁盘上.其标识为:"文件名.c". 编 ...

  8. 力扣578(MySQL)-查询回答率最高的问题(中等)

    题目: 从 survey_log 表中获得回答率最高的问题,survey_log 表包含这些列:id, action, question_id, answer_id, q_num, timestamp ...

  9. 技术解读:现代化工具链在大规模 C++ 项目中的运用 | 龙蜥技术

    简介: 本文详细介绍我们在实际工作中的大型 C++ 项目中现代化工具链的实践以及结果. 编者按:C++ 语言与编译器一直都在持续演进,出现了许多令人振奋的新特性,同时还有许多新特性在孵化阶.除此之外, ...

  10. 慢sql治理经典案例分享

    ​简介:菜鸟供应链金融慢sql治理已经有一段时间,自己负责的应用持续很长时间没有慢sql告警,现阶段在推进组内其他成员治理应用慢sql.这里把治理过程中的一些实践拿出来分享下. ​ 作者 | 如期 来 ...