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.提交请求后会发现控制台报错,但是数据库里面并没有插入数据。
文章知识点与官方知识档案匹配,可进一步学习相关知识