springboot+mybatis多数据源的事务问题
1.springboot+mybatis实现多数据源后,针对单个数据源我们可以使用@Transactional(name="xxxTransactionManager")
来指定使用的事务管理器,但是如果被注解的方法需要同时支持两个事务管理器呢,这个时候如果用@Transactional注解就不
合适了,属性name只支持字符串类型,所以只能填一个,如果不传name属性,而且项目没有配置默认的事务管理器,在调用方
法时则会抛出未指定默认事务管理器的异常。这个时候,我们可以用编程式事务来解决这个问题。
2.多数据源配置已经完成,但是还没有事务管理器,所以先配置支持事务。
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource;
/**
* 数据源1
*/
@Configuration
@MapperScan(basePackages = "com.example.mybatis.mapper",sqlSessionFactoryRef = "sqlSessionFactoryOne")
public class DataSourceConfigOne { @Bean(name = "dataSourceOne")
@Primary// 表示这个数据源是默认数据源
// 读取application.properties中的配置参数映射成为一个对象,prefix表示参数的前缀
@ConfigurationProperties(prefix = "spring.datasource.one")
public DataSource dataSourceOne() {
return DataSourceBuilder.create().build();
} @Bean(name = "sqlSessionFactoryOne")
@Primary
public SqlSessionFactory sqlSessionFactoryOne(@Qualifier("dataSourceOne") DataSource datasource)throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
// 设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return bean.getObject();
}
@Bean(name = "transactionManagerOne")
public PlatformTransactionManager transactionManagerOne(@Qualifier("dataSourceOne") DataSource dataSourceOne) {
return new DataSourceTransactionManager(dataSourceOne);
} @Primary
public SqlSessionTemplate sqlsessiontemplateOne(@Qualifier("sqlsessiontemplateOne") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource;
/**
* 数据源2
*/
@Configuration
@MapperScan(basePackages = "com.example.mybatis.mapper2",sqlSessionFactoryRef = "sqlSessionFactoryTwo")
public class DataSourceConfigTwo {
@Bean(name = "dataSourceTwo")
// 读取application.properties中的配置参数映射成为一个对象,prefix表示参数的前缀
@ConfigurationProperties(prefix = "spring.datasource.two")
public DataSource dataSourceTwo() {
return DataSourceBuilder.create().build();
} @Bean(name = "sqlSessionFactoryTwo")
public SqlSessionFactory sqlSessionFactoryTwo(@Qualifier("dataSourceTwo") DataSource datasource)throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
// 设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath:mapper2/*.xml"));
bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);//下划线-驼峰映射
return bean.getObject();
}
@Bean(name = "transactionManagerTwo")
public PlatformTransactionManager transactionManagerTwo(@Qualifier("dataSourceTwo") DataSource dataSourceTwo) {
return new DataSourceTransactionManager(dataSourceTwo);
} public SqlSessionTemplate sqlsessiontemplateTwo(@Qualifier("sqlsessiontemplateTwo") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
3.使用自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface MoreTransaction {
String[] value() default {};
}
4.事务切面
@Aspect
@Component
public class TransactionAop {
@Pointcut("@annotation(com.example.mybatis.config.aop.annotation.MoreTransaction)")
public void MoreTransaction() {
} @Pointcut("execution(* com.example.mybatis.controller.*.*(..))")
public void excudeController() {
} @Around(value = "MoreTransaction()&&excudeController()&&@annotation(annotation)")
public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, MoreTransaction annotation) throws Throwable {
Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<>();
Stack<TransactionStatus> transactionStatuStack = new Stack<>(); try {
if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, annotation)) {
return null;
}
Object ret = thisJoinPoint.proceed();
commit(dataSourceTransactionManagerStack, transactionStatuStack);
return ret;
} catch (Throwable e) {
rollback(dataSourceTransactionManagerStack, transactionStatuStack);
log.error(String.format("MultiTransactionalAspect, method:%s-%s occors error:",
thisJoinPoint.getTarget().getClass().getSimpleName(), thisJoinPoint.getSignature().getName()), e);
throw e;
}
} /**
* 开启事务处理方法
*
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
* @param multiTransactional
* @return
*/
private boolean openTransaction(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
Stack<TransactionStatus> transactionStatuStack,MoreTransaction multiTransactional) { String[] transactionMangerNames = multiTransactional.value();
if (ArrayUtils.isEmpty(multiTransactional.value())) {
return false;
}
for (String beanName : transactionMangerNames) {
DataSourceTransactionManager dataSourceTransactionManager =(DataSourceTransactionManager) SpringContextUtil.getBean(beanName);
TransactionStatus transactionStatus = dataSourceTransactionManager
.getTransaction(new DefaultTransactionDefinition());
transactionStatuStack.push(transactionStatus);
dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
}
return true;
} /**
* 提交处理方法
*
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
*/
private void commit(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
Stack<TransactionStatus> transactionStatuStack) {
while (!dataSourceTransactionManagerStack.isEmpty()) {
dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
}
} /**
* 回滚处理方法
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
*/
private void rollback(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
Stack<TransactionStatus> transactionStatuStack) {
while (!dataSourceTransactionManagerStack.isEmpty()) {
dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
}
}
}
5.service层两个我要新增的方法,这两个方法是两个数据源(一个是我本地的,一个是阿里云的)
@Override
public int addUserInfo(UserInfo record) {
return userInfoMapper.insert(new UserInfo().setId(UUID.randomUUID().toString()).setUserAccount("吾问无为谓"));
}
@Override
public int addUser(User record) {
return userMapper.insertSelective(record);
}
6.controller层的调用方法上使用事务注解,将两个数据源的事务管理器名字作为参数传入。
@GetMapping("/dataSourceList")
@ResponseBody
@MoreTransaction(value = {"transactionManagerOne","transactionManagerTwo"})
public ResultData getDataSourceList(){
int i=userService.addUser(new User().setPhone("11111111111").setUserName("哈哈哈"));
int a=1/0;
int k=userService.addUserInfo(new UserInfo().setId(UUID.randomUUID().toString()).setUserAccount("吾问无为谓"));
Map map=new HashMap();
map.put("k",k);
return ResultData.success(map);
}
7.提交请求后会发现控制台报错,但是数据库里面并没有插入数据。


参考链接:https://blog.csdn.net/qq_34322777/article/details/80833935
springboot+mybatis多数据源的事务问题的更多相关文章
- spring-boot (四) springboot+mybatis多数据源最简解决方案
学习文章来自:http://www.ityouknow.com/spring-boot.html 配置文件 pom包就不贴了比较简单该依赖的就依赖,主要是数据库这边的配置: mybatis.confi ...
- springboot + mybatis + 多数据源
此文已由作者赵计刚薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验 在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1) ...
- springboot+atomikos+多数据源管理事务(mysql 8.0)
jta:Java Transaction API,即是java中对事务处理的api 即 api即是接口的意思 atomikos:Atomikos TransactionsEssentials 是一个为 ...
- 第九章 springboot + mybatis + 多数据源 (AOP实现)
在第八章 springboot + mybatis + 多数据源代码的基础上,做两点修改 1.ShopDao package com.xxx.firstboot.dao; import org.spr ...
- springboot mybatis 多数据源配置
首先导入mybatis等包,这里就不多说. 下面是配置多数据源和mybatis,每个数据源对应一套mybatis模板 数据源1: package com.aaaaaaa.config.datasour ...
- 【第九章】 springboot + mybatis + 多数据源 (AOP实现)
在第八章 springboot + mybatis + 多数据源代码的基础上,做两点修改 1.ShopDao package com.xxx.firstboot.dao; import org.spr ...
- 第八章 springboot + mybatis + 多数据源(转载)
本篇博客转发自:http://www.cnblogs.com/java-zhao/p/5413845.html 在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构 ...
- 【第八章】 springboot + mybatis + 多数据源
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseConte ...
- 第八章 springboot + mybatis + 多数据源2(解决循环引用)
解决了循环引用 1.application.properties #the first datasource jdbc.names:1,2 jdbc1.driverClassName = com.my ...
- spring boot(七):springboot+mybatis多数据源最简解决方案
说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们项目是后者的模式,网上找了很多,大都是根据jpa来做多数据源解决方案,要不就是老的spring多数据源解 ...
随机推荐
- 基于GPU 显卡在k8s 集群上实现hpa 功能
前言 Kubernetes 支持HPA模块进行容器伸缩,默认支持CPU和内存等指标.原生的HPA基于Heapster,不支持GPU指标的伸缩,但是支持通过CustomMetrics的方式进行HPA指标 ...
- windows下搭建h5游戏小小火影教程【附安装包】
小小三国是一款很好玩的H5游戏.只需要用一台64位的windows电脑或服务器,你就可以和朋友们一起联机玩这个游戏了.本文将会教你如何搭建这个游戏,步骤如下. 1.下载好后你会看到该压缩包 2.解压到 ...
- 【面试题】ES6语法五之箭头函数
ES6特性=>. function foo(x, y){ return x + y } var foo = (x, y) => x + y 包括一个参数列表(零个或多个参数,如果参数不是一 ...
- SP2-0750: You may need to set ORACLE_HOME to your Oracle software directory 解决办法
[oracle@localhost oracle]$ ./sqlplus Error 6 initializing SQL*Plus Message file sp1<lang>.msb ...
- npm 局部安装 jbrowse
#此处不要加 -g参数 , -g 是全局安装 npm install @jbrowse/cli #用npx 才能使用这个包,无法按照官方文档直接使用 npx jbrowse --version
- 微服务减少jar包体积
<build> <finalName>${project.artifactId}</finalName> <plugins> <!--打包jar- ...
- Windows 与Docker
https://docs.microsoft.com/zh-cn/windows/wsl/install-manual#step-4---download-the-linux-kernel-updat ...
- 组件中的data为什么不是一个对象而是一个函数?
组件中的data为什么不是一个对象而是一个函数? 组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的,基于 ...
- Android版本历史
版本 Code Name 代号 API 日期 NDK 1.0 Astro 铁臂阿童木 1 1.1 Bender, Petit Four 发条机器人 2 1.5 Cupcake 纸杯 ...
- path.resolve和path.resolve的用法
前言:要搞清楚path.join()和path.resolve的具体作用,最好自己搞个文件,用node跑一遍去测试一下.只有自己亲自动手实践了,才知道具体是怎么回事,才能真正的理解 一.path.jo ...