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多数据源的事务问题的更多相关文章

  1. spring-boot (四) springboot+mybatis多数据源最简解决方案

    学习文章来自:http://www.ityouknow.com/spring-boot.html 配置文件 pom包就不贴了比较简单该依赖的就依赖,主要是数据库这边的配置: mybatis.confi ...

  2. springboot + mybatis + 多数据源

    此文已由作者赵计刚薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验 在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1) ...

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

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

  4. 第九章 springboot + mybatis + 多数据源 (AOP实现)

    在第八章 springboot + mybatis + 多数据源代码的基础上,做两点修改 1.ShopDao package com.xxx.firstboot.dao; import org.spr ...

  5. springboot mybatis 多数据源配置

    首先导入mybatis等包,这里就不多说. 下面是配置多数据源和mybatis,每个数据源对应一套mybatis模板 数据源1: package com.aaaaaaa.config.datasour ...

  6. 【第九章】 springboot + mybatis + 多数据源 (AOP实现)

    在第八章 springboot + mybatis + 多数据源代码的基础上,做两点修改 1.ShopDao package com.xxx.firstboot.dao; import org.spr ...

  7. 第八章 springboot + mybatis + 多数据源(转载)

    本篇博客转发自:http://www.cnblogs.com/java-zhao/p/5413845.html 在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构 ...

  8. 【第八章】 springboot + mybatis + 多数据源

    在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseConte ...

  9. 第八章 springboot + mybatis + 多数据源2(解决循环引用)

    解决了循环引用 1.application.properties #the first datasource jdbc.names:1,2 jdbc1.driverClassName = com.my ...

  10. spring boot(七):springboot+mybatis多数据源最简解决方案

    说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们项目是后者的模式,网上找了很多,大都是根据jpa来做多数据源解决方案,要不就是老的spring多数据源解 ...

随机推荐

  1. vue下载zip包到本地

    <script> import axios from 'axios' export default{ methods: { downloadZip (downloadName, downl ...

  2. 在dockerfile使用定时任务遇见的坑

    使用dockerfile做容器时没有问题,但是要在里面加上定时任务时遇见了情况 dockerfile 中 写法  CMD cron 情况1:容器运行起来后 定时任务不生效 情况2:容器起来后定时任务生 ...

  3. iOS开发之从UIColo到十六进制

    今天开发中需要将UIColor转为十六进制字符串,记录下修改代码 UIColor *color = [UIColor blueColor]; const CGFloat* components = C ...

  4. Veeva_001常见问题总结

    1.时间互动轴:其他代表与该医生的互动记录 2.Call的拜访流程 : 用户在Ipad端先制定计划拜访(已计划:青色)后,需手动同步到服务器上: 进入拜访详细,选择联系人和产品及相关拜访信息等,并'保 ...

  5. angular 路由守卫Observable异步请求串联

    假设路由守卫有这种场景 需要使用observable同时发送多个Http 请求,判断request2返回的数据中是否存在request1返回的数据 使用async await export class ...

  6. HCIP-ICT实战进阶05-路由策略与策略路由

    HCIP-ICT实战进阶05-路由策略与策略路由 0 前言 什么是路由策略? 基于报文的目的IP地址进行路由表查找, 之后转发数据; 针对控制平面, 为路由协议和路由表服务, 针对路由信息进行过滤或者 ...

  7. Ansible基础认识及安装使用详解

  8. 公司官网百度搜素优化(www.curetech.cc)

    1.  解读" 百度搜素引擎网页质量白皮书 "  . 链接:https://pan.baidu.com/s/1fD7Cm93qsK01M0K1M1cIKw 提取码:9krx 2.  ...

  9. JSR303校验

    JSR是Java Specification Requests的缩写,意思是Java 规范提案.是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求.任何人 ...

  10. 关于MYSQL知识点复习

    关于MYSQL关联查询JOIN:   https://www.cnblogs.com/withscorpion/p/9454490.html